Merge pull request #4857 from t-nelson/Gotham_13.2_backports
[vuplus_xbmc] / xbmc / network / AirTunesServer.cpp
1 /*
2  * Many concepts and protocol specification in this code are taken
3  * from Shairport, by James Laird.
4  *
5  *      Copyright (C) 2011-2013 Team XBMC
6  *      http://xbmc.org
7  *
8  *  This Program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2.1, or (at your option)
11  *  any later version.
12  *
13  *  This Program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with XBMC; see the file COPYING.  If not, see
20  *  <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #include "network/Network.h"
25 #if !defined(TARGET_WINDOWS)
26 #pragma GCC diagnostic ignored "-Wwrite-strings"
27 #endif
28
29 #include "AirTunesServer.h"
30
31 #ifdef HAS_AIRPLAY
32 #include "network/AirPlayServer.h"
33 #endif
34
35 #ifdef HAS_AIRTUNES
36
37 #include "utils/log.h"
38 #include "utils/StdString.h"
39 #include "network/Zeroconf.h"
40 #include "ApplicationMessenger.h"
41 #include "filesystem/PipeFile.h"
42 #include "Application.h"
43 #include "cores/dvdplayer/DVDDemuxers/DVDDemuxBXA.h"
44 #include "filesystem/File.h"
45 #include "music/tags/MusicInfoTag.h"
46 #include "FileItem.h"
47 #include "GUIInfoManager.h"
48 #include "guilib/GUIWindowManager.h"
49 #include "utils/Variant.h"
50 #include "utils/StringUtils.h"
51 #include "settings/AdvancedSettings.h"
52 #include "settings/Settings.h"
53 #include "utils/EndianSwap.h"
54 #include "URL.h"
55 #include "interfaces/AnnouncementManager.h"
56
57 #include <map>
58 #include <string>
59
60 #define TMP_COVERART_PATH "special://temp/airtunes_album_thumb.jpg"
61
62 using namespace XFILE;
63 using namespace ANNOUNCEMENT;
64
65 #if defined(HAVE_LIBSHAIRPLAY)
66 DllLibShairplay *CAirTunesServer::m_pLibShairplay = NULL;
67 #else
68 DllLibShairport *CAirTunesServer::m_pLibShairport = NULL;
69 #endif
70 CAirTunesServer *CAirTunesServer::ServerInstance = NULL;
71 CStdString CAirTunesServer::m_macAddress;
72 std::string CAirTunesServer::m_metadata[3];
73 CCriticalSection CAirTunesServer::m_metadataLock;
74 bool CAirTunesServer::m_streamStarted = false;
75
76 //parse daap metadata - thx to project MythTV
77 std::map<std::string, std::string> decodeDMAP(const char *buffer, unsigned int size)
78 {
79   std::map<std::string, std::string> result;
80   unsigned int offset = 8;
81   while (offset < size)
82   {
83     std::string tag;
84     tag.append(buffer + offset, 4);
85     offset += 4;
86     uint32_t length = Endian_SwapBE32(*(uint32_t *)(buffer + offset));
87     offset += sizeof(uint32_t);
88     std::string content;
89     content.append(buffer + offset, length);//possible fixme - utf8?
90     offset += length;
91     result[tag] = content;
92   }
93   return result;
94 }
95
96 void CAirTunesServer::RefreshMetadata()
97 {
98   CSingleLock lock(m_metadataLock);
99   MUSIC_INFO::CMusicInfoTag tag;
100   if (m_metadata[0].length())
101     tag.SetAlbum(m_metadata[0]);//album
102   if (m_metadata[1].length())
103     tag.SetTitle(m_metadata[1]);//title
104   if (m_metadata[2].length())
105     tag.SetArtist(m_metadata[2]);//artist
106   
107   CApplicationMessenger::Get().SetCurrentSongTag(tag);
108 }
109
110 void CAirTunesServer::RefreshCoverArt()
111 {
112   CSingleLock lock(m_metadataLock);
113   //reset to empty before setting the new one
114   //else it won't get refreshed because the name didn't change
115   g_infoManager.SetCurrentAlbumThumb("");
116   //update the ui
117   g_infoManager.SetCurrentAlbumThumb(TMP_COVERART_PATH);
118   //update the ui
119   CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_REFRESH_THUMBS);
120   g_windowManager.SendThreadMessage(msg);
121 }
122
123 void CAirTunesServer::SetMetadataFromBuffer(const char *buffer, unsigned int size)
124 {
125
126   std::map<std::string, std::string> metadata = decodeDMAP(buffer, size);
127   CSingleLock lock(m_metadataLock);
128
129   if(metadata["asal"].length())
130     m_metadata[0] = metadata["asal"];//album
131   if(metadata["minm"].length())    
132     m_metadata[1] = metadata["minm"];//title
133   if(metadata["asar"].length())    
134     m_metadata[2] = metadata["asar"];//artist
135   
136   RefreshMetadata();
137 }
138
139 void CAirTunesServer::Announce(AnnouncementFlag flag, const char *sender, const char *message, const CVariant &data)
140 {
141   if ( (flag & Player) && strcmp(sender, "xbmc") == 0)
142   {
143     if (strcmp(message, "OnPlay") == 0 && m_streamStarted)
144     {
145       RefreshMetadata();
146       RefreshCoverArt();
147     }
148   }
149 }
150
151 void CAirTunesServer::SetCoverArtFromBuffer(const char *buffer, unsigned int size)
152 {
153   XFILE::CFile tmpFile;
154
155   if(!size)
156     return;
157
158   CSingleLock lock(m_metadataLock);
159   
160   if (tmpFile.OpenForWrite(TMP_COVERART_PATH, true))
161   {
162     int writtenBytes=0;
163     writtenBytes = tmpFile.Write(buffer, size);
164     tmpFile.Close();
165
166     if(writtenBytes)
167     {
168       RefreshCoverArt();
169     }
170   }
171 }
172
173 #if defined(HAVE_LIBSHAIRPLAY)
174 #define RSA_KEY " \
175 -----BEGIN RSA PRIVATE KEY-----\
176 MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\
177 wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\
178 wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\
179 /+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\
180 UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\
181 BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\
182 LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\
183 NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\
184 lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\
185 aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\
186 a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\
187 oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\
188 oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\
189 k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\
190 AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\
191 cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\
192 54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\
193 17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\
194 1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\
195 LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\
196 2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\
197 -----END RSA PRIVATE KEY-----"
198
199 void CAirTunesServer::AudioOutputFunctions::audio_set_metadata(void *cls, void *session, const void *buffer, int buflen)
200 {
201   CAirTunesServer::SetMetadataFromBuffer((char *)buffer, buflen);
202 }
203
204 void CAirTunesServer::AudioOutputFunctions::audio_set_coverart(void *cls, void *session, const void *buffer, int buflen)
205 {
206   CAirTunesServer::SetCoverArtFromBuffer((char *)buffer, buflen);
207 }
208
209 char *session="XBMC-AirTunes";
210
211 void* CAirTunesServer::AudioOutputFunctions::audio_init(void *cls, int bits, int channels, int samplerate)
212 {
213   XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
214   pipe->OpenForWrite(XFILE::PipesManager::GetInstance().GetUniquePipeName());
215   pipe->SetOpenThreashold(300);
216
217   Demux_BXA_FmtHeader header;
218   strncpy(header.fourcc, "BXA ", 4);
219   header.type = BXA_PACKET_TYPE_FMT_DEMUX;
220   header.bitsPerSample = bits;
221   header.channels = channels;
222   header.sampleRate = samplerate;
223   header.durationMs = 0;
224
225   if (pipe->Write(&header, sizeof(header)) == 0)
226     return 0;
227
228   ThreadMessage tMsg = { TMSG_MEDIA_STOP };
229   CApplicationMessenger::Get().SendMessage(tMsg, true);
230
231   CFileItem item;
232   item.SetPath(pipe->GetName());
233   item.SetMimeType("audio/x-xbmc-pcm");
234   m_streamStarted = true;
235
236   CApplicationMessenger::Get().PlayFile(item);
237
238   return session;//session
239 }
240
241 void  CAirTunesServer::AudioOutputFunctions::audio_set_volume(void *cls, void *session, float volume)
242 {
243   //volume from -30 - 0 - -144 means mute
244   float volPercent = volume < -30.0f ? 0 : 1 - volume/-30;
245 #ifdef HAS_AIRPLAY
246   CAirPlayServer::backupVolume();
247 #endif
248   if (CSettings::Get().GetBool("services.airplayvolumecontrol"))
249     g_application.SetVolume(volPercent, false);//non-percent volume 0.0-1.0
250 }
251
252 void  CAirTunesServer::AudioOutputFunctions::audio_process(void *cls, void *session, const void *buffer, int buflen)
253 {
254   #define NUM_OF_BYTES 64
255   XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
256   int sentBytes = 0;
257   unsigned char buf[NUM_OF_BYTES];
258
259   while (sentBytes < buflen)
260   {
261     int n = (buflen - sentBytes < NUM_OF_BYTES ? buflen - sentBytes : NUM_OF_BYTES);
262     memcpy(buf, (char*) buffer + sentBytes, n);
263
264     if (pipe->Write(buf, n) == 0)
265       return;
266
267     sentBytes += n;
268   }
269 }
270
271 void  CAirTunesServer::AudioOutputFunctions::audio_flush(void *cls, void *session)
272 {
273   XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
274   pipe->Flush();
275 }
276
277 void  CAirTunesServer::AudioOutputFunctions::audio_destroy(void *cls, void *session)
278 {
279   XFILE::CPipeFile *pipe=(XFILE::CPipeFile *)cls;
280   pipe->SetEof();
281   pipe->Close();
282
283   //fix airplay video for ios5 devices
284   //on ios5 when airplaying video
285   //the client first opens an airtunes stream
286   //while the movie is loading
287   //in that case we don't want to stop the player here
288   //because this would stop the airplaying video
289 #ifdef HAS_AIRPLAY
290   if (!CAirPlayServer::IsPlaying())
291 #endif
292   {
293     ThreadMessage tMsg = { TMSG_MEDIA_STOP };
294     CApplicationMessenger::Get().SendMessage(tMsg, true);
295     CLog::Log(LOGDEBUG, "AIRTUNES: AirPlay not running - stopping player");
296   }
297   
298   m_streamStarted = false;
299 }
300
301 void shairplay_log(void *cls, int level, const char *msg)
302 {
303   int xbmcLevel = LOGINFO;
304
305   switch(level)
306   {
307     case RAOP_LOG_EMERG:    // system is unusable 
308       xbmcLevel = LOGFATAL;
309       break;
310     case RAOP_LOG_ALERT:    // action must be taken immediately
311     case RAOP_LOG_CRIT:     // critical conditions
312       xbmcLevel = LOGSEVERE;
313       break;
314     case RAOP_LOG_ERR:      // error conditions
315       xbmcLevel = LOGERROR;
316       break;
317     case RAOP_LOG_WARNING:  // warning conditions
318       xbmcLevel = LOGWARNING;
319       break;
320     case RAOP_LOG_NOTICE:   // normal but significant condition
321       xbmcLevel = LOGNOTICE;
322       break;
323     case RAOP_LOG_INFO:     // informational
324       xbmcLevel = LOGINFO;
325       break;
326     case RAOP_LOG_DEBUG:    // debug-level messages
327       xbmcLevel = LOGDEBUG;
328       break;
329     default:
330       break;
331   }
332     CLog::Log(xbmcLevel, "AIRTUNES: %s", msg);
333 }
334
335 #else
336
337 struct ao_device_xbmc
338 {
339   XFILE::CPipeFile *pipe;
340 };
341
342 //audio output interface
343 void CAirTunesServer::AudioOutputFunctions::ao_initialize(void)
344 {
345 }
346
347 void  CAirTunesServer::AudioOutputFunctions::ao_set_volume(float volume)
348 {
349   //volume from -30 - 0 - -144 means mute
350   float volPercent = volume < -30.0f ? 0 : 1 - volume/-30;
351 #ifdef HAS_AIRPLAY
352   CAirPlayServer::backupVolume();
353 #endif
354   if (CSettings::Get().GetBool("services.airplayvolumecontrol"))
355     g_application.SetVolume(volPercent, false);//non-percent volume 0.0-1.0
356 }
357
358
359 int CAirTunesServer::AudioOutputFunctions::ao_play(ao_device *device, char *output_samples, uint32_t num_bytes)
360 {
361   if (!device)
362     return 0;
363
364   /*if (num_bytes && g_application.m_pPlayer->HasPlayer())
365     g_application.m_pPlayer->SetCaching(CACHESTATE_NONE);*///TODO
366
367   ao_device_xbmc* device_xbmc = (ao_device_xbmc*) device;
368
369 #define NUM_OF_BYTES 64
370
371   unsigned int sentBytes = 0;
372   unsigned char buf[NUM_OF_BYTES];
373   while (sentBytes < num_bytes)
374   {
375     int n = (num_bytes - sentBytes < NUM_OF_BYTES ? num_bytes - sentBytes : NUM_OF_BYTES);
376     memcpy(buf, (char*) output_samples + sentBytes, n);
377
378     if (device_xbmc->pipe->Write(buf, n) == 0)
379       return 0;
380
381     sentBytes += n;
382   }
383
384   return 1;
385 }
386
387 int CAirTunesServer::AudioOutputFunctions::ao_default_driver_id(void)
388 {
389   return 0;
390 }
391
392 ao_device* CAirTunesServer::AudioOutputFunctions::ao_open_live(int driver_id, ao_sample_format *format,
393     ao_option *option)
394 {
395   ao_device_xbmc* device = new ao_device_xbmc();
396
397   device->pipe = new XFILE::CPipeFile;
398   device->pipe->OpenForWrite(XFILE::PipesManager::GetInstance().GetUniquePipeName());
399   device->pipe->SetOpenThreashold(300);
400
401   Demux_BXA_FmtHeader header;
402   strncpy(header.fourcc, "BXA ", 4);
403   header.type = BXA_PACKET_TYPE_FMT_DEMUX;
404   header.bitsPerSample = format->bits;
405   header.channels = format->channels;
406   header.sampleRate = format->rate;
407   header.durationMs = 0;
408
409   if (device->pipe->Write(&header, sizeof(header)) == 0)
410   {
411     delete device->pipe;
412     delete device;
413     return 0;
414   }
415   
416   ThreadMessage tMsg = { TMSG_MEDIA_STOP };
417   CApplicationMessenger::Get().SendMessage(tMsg, true);
418
419   CFileItem item;
420   item.SetPath(device->pipe->GetName());
421   item.SetMimeType("audio/x-xbmc-pcm");
422
423   if (ao_get_option(option, "artist"))
424     item.GetMusicInfoTag()->SetArtist(ao_get_option(option, "artist"));
425
426   if (ao_get_option(option, "album"))
427     item.GetMusicInfoTag()->SetAlbum(ao_get_option(option, "album"));
428
429   if (ao_get_option(option, "name"))
430     item.GetMusicInfoTag()->SetTitle(ao_get_option(option, "name"));
431
432   m_streamStarted = true;
433
434   CApplicationMessenger::Get().PlayFile(item);
435
436   return (ao_device*) device;
437 }
438
439 int CAirTunesServer::AudioOutputFunctions::ao_close(ao_device *device)
440 {
441   ao_device_xbmc* device_xbmc = (ao_device_xbmc*) device;
442   device_xbmc->pipe->SetEof();
443   device_xbmc->pipe->Close();
444   delete device_xbmc->pipe;
445
446   //fix airplay video for ios5 devices
447   //on ios5 when airplaying video
448   //the client first opens an airtunes stream
449   //while the movie is loading
450   //in that case we don't want to stop the player here
451   //because this would stop the airplaying video
452 #ifdef HAS_AIRPLAY
453   if (!CAirPlayServer::IsPlaying())
454 #endif
455   {
456     ThreadMessage tMsg = { TMSG_MEDIA_STOP };
457     CApplicationMessenger::Get().SendMessage(tMsg, true);
458     CLog::Log(LOGDEBUG, "AIRTUNES: AirPlay not running - stopping player");
459   }
460
461   delete device_xbmc;
462   m_streamStarted = false;
463
464   return 0;
465 }
466
467 void CAirTunesServer::AudioOutputFunctions::ao_set_metadata(const char *buffer, unsigned int size)
468 {
469   CAirTunesServer::SetMetadataFromBuffer(buffer, size);
470 }
471
472 void CAirTunesServer::AudioOutputFunctions::ao_set_metadata_coverart(const char *buffer, unsigned int size)
473 {
474   CAirTunesServer::SetCoverArtFromBuffer(buffer, size);
475 }
476
477 /* -- Device Setup/Playback/Teardown -- */
478 int CAirTunesServer::AudioOutputFunctions::ao_append_option(ao_option **options, const char *key, const char *value)
479 {
480   ao_option *op, *list;
481
482   op = (ao_option*) calloc(1,sizeof(ao_option));
483   if (op == NULL) return 0;
484
485   op->key = strdup(key);
486   op->value = strdup(value?value:"");
487   op->next = NULL;
488
489   if ((list = *options) != NULL)
490   {
491     list = *options;
492     while (list->next != NULL)
493       list = list->next;
494     list->next = op;
495   }
496   else
497   {
498     *options = op;
499   }
500
501   return 1;
502 }
503
504 void CAirTunesServer::AudioOutputFunctions::ao_free_options(ao_option *options)
505 {
506   ao_option *rest;
507
508   while (options != NULL)
509   {
510     rest = options->next;
511     free(options->key);
512     free(options->value);
513     free(options);
514     options = rest;
515   }
516 }
517
518 char* CAirTunesServer::AudioOutputFunctions::ao_get_option(ao_option *options, const char* key)
519 {
520
521   while (options != NULL)
522   {
523     if (strcmp(options->key, key) == 0)
524       return options->value;
525     options = options->next;
526   }
527
528   return NULL;
529 }
530
531 int shairport_log(const char* msg, size_t msgSize)
532 {
533   if( g_advancedSettings.m_logEnableAirtunes)
534   {
535     CLog::Log(LOGDEBUG, "AIRTUNES: %s", msg);
536   }
537   return 1;
538 }
539
540 #endif
541
542 bool CAirTunesServer::StartServer(int port, bool nonlocal, bool usePassword, const CStdString &password/*=""*/)
543 {
544   bool success = false;
545   CStdString pw = password;
546   CNetworkInterface *net = g_application.getNetwork().GetFirstConnectedInterface();
547   StopServer(true);
548
549   if (net)
550   {
551     m_macAddress = net->GetMacAddress();
552     StringUtils::Replace(m_macAddress, ":","");
553     while (m_macAddress.size() < 12)
554     {
555       m_macAddress = CStdString("0") + m_macAddress;
556     }
557   }
558   else
559   {
560     m_macAddress = "000102030405";
561   }
562
563   if (!usePassword)
564   {
565     pw.clear();
566   }
567
568   ServerInstance = new CAirTunesServer(port, nonlocal);
569   if (ServerInstance->Initialize(pw))
570   {
571 #if !defined(HAVE_LIBSHAIRPLAY)
572     ServerInstance->Create();
573 #endif
574     success = true;
575   }
576
577   if (success)
578   {
579     CStdString appName = StringUtils::Format("%s@%s",
580                                              m_macAddress.c_str(),
581                                              g_infoManager.GetLabel(SYSTEM_FRIENDLY_NAME).c_str());
582
583     std::vector<std::pair<std::string, std::string> > txt;
584     txt.push_back(std::make_pair("txtvers",  "1"));
585     txt.push_back(std::make_pair("cn", "0,1"));
586     txt.push_back(std::make_pair("ch", "2"));
587     txt.push_back(std::make_pair("ek", "1"));
588     txt.push_back(std::make_pair("et", "0,1"));
589     txt.push_back(std::make_pair("sv", "false"));
590     txt.push_back(std::make_pair("tp",  "UDP"));
591     txt.push_back(std::make_pair("sm",  "false"));
592     txt.push_back(std::make_pair("ss",  "16"));
593     txt.push_back(std::make_pair("sr",  "44100"));
594     txt.push_back(std::make_pair("pw",  usePassword?"true":"false"));
595     txt.push_back(std::make_pair("vn",  "3"));
596     txt.push_back(std::make_pair("da",  "true"));
597     txt.push_back(std::make_pair("vs",  "130.14"));
598     txt.push_back(std::make_pair("md",  "0,1,2"));
599     txt.push_back(std::make_pair("am",  "Xbmc,1"));
600
601     CZeroconf::GetInstance()->PublishService("servers.airtunes", "_raop._tcp", appName, port, txt);
602   }
603
604   return success;
605 }
606
607 void CAirTunesServer::StopServer(bool bWait)
608 {
609   if (ServerInstance)
610   {
611 #if !defined(HAVE_LIBSHAIRPLAY)
612     if (m_pLibShairport->IsLoaded())
613     {
614       m_pLibShairport->shairport_exit();
615     }
616 #endif
617     ServerInstance->StopThread(bWait);
618     ServerInstance->Deinitialize();
619     if (bWait)
620     {
621       delete ServerInstance;
622       ServerInstance = NULL;
623     }
624
625     CZeroconf::GetInstance()->RemoveService("servers.airtunes");
626   }
627 }
628
629  bool CAirTunesServer::IsRunning()
630  {
631    if (ServerInstance == NULL)
632      return false;
633
634    return ((CThread*)ServerInstance)->IsRunning();
635  }
636
637 CAirTunesServer::CAirTunesServer(int port, bool nonlocal) : CThread("AirTunesServer")
638 {
639   m_port = port;
640 #if defined(HAVE_LIBSHAIRPLAY)
641   m_pLibShairplay = new DllLibShairplay();
642   m_pPipe         = new XFILE::CPipeFile;  
643 #else
644   m_pLibShairport = new DllLibShairport();
645 #endif
646   CAnnouncementManager::AddAnnouncer(this);
647 }
648
649 CAirTunesServer::~CAirTunesServer()
650 {
651 #if defined(HAVE_LIBSHAIRPLAY)
652   if (m_pLibShairplay->IsLoaded())
653   {
654     m_pLibShairplay->Unload();
655   }
656   delete m_pLibShairplay;
657   delete m_pPipe;
658 #else
659   if (m_pLibShairport->IsLoaded())
660   {
661     m_pLibShairport->Unload();
662   }
663   delete m_pLibShairport;
664 #endif
665   CAnnouncementManager::RemoveAnnouncer(this);
666 }
667
668 void CAirTunesServer::Process()
669 {
670   m_bStop = false;
671
672 #if !defined(HAVE_LIBSHAIRPLAY)
673   while (!m_bStop && m_pLibShairport->shairport_is_running())
674   {
675     m_pLibShairport->shairport_loop();
676   }
677 #endif
678 }
679
680 bool CAirTunesServer::Initialize(const CStdString &password)
681 {
682   bool ret = false;
683
684   Deinitialize();
685
686 #if defined(HAVE_LIBSHAIRPLAY)
687   if (m_pLibShairplay->Load())
688   {
689
690     raop_callbacks_t ao;
691     ao.cls                  = m_pPipe;
692     ao.audio_init           = AudioOutputFunctions::audio_init;
693     ao.audio_set_volume     = AudioOutputFunctions::audio_set_volume;
694     ao.audio_set_metadata   = AudioOutputFunctions::audio_set_metadata;
695     ao.audio_set_coverart   = AudioOutputFunctions::audio_set_coverart;
696     ao.audio_process        = AudioOutputFunctions::audio_process;
697     ao.audio_flush          = AudioOutputFunctions::audio_flush;
698     ao.audio_destroy        = AudioOutputFunctions::audio_destroy;
699     m_pLibShairplay->EnableDelayedUnload(false);
700     m_pRaop = m_pLibShairplay->raop_init(1, &ao, RSA_KEY);//1 - we handle one client at a time max
701     ret = m_pRaop != NULL;    
702
703     if(ret)
704     {
705       char macAdr[6];    
706       unsigned short port = (unsigned short)m_port;
707       
708       m_pLibShairplay->raop_set_log_level(m_pRaop, RAOP_LOG_WARNING);
709       if(g_advancedSettings.m_logEnableAirtunes)
710       {
711         m_pLibShairplay->raop_set_log_level(m_pRaop, RAOP_LOG_DEBUG);
712       }
713
714       m_pLibShairplay->raop_set_log_callback(m_pRaop, shairplay_log, NULL);
715
716       CNetworkInterface *net = g_application.getNetwork().GetFirstConnectedInterface();
717
718       if (net)
719       {
720         net->GetMacAddressRaw(macAdr);
721       }
722
723       ret = m_pLibShairplay->raop_start(m_pRaop, &port, macAdr, 6, password.c_str()) >= 0;
724     }
725   }
726
727 #else
728
729   int numArgs = 3;
730   CStdString hwStr;
731   CStdString pwStr;
732   CStdString portStr;
733
734   hwStr = StringUtils::Format("--mac=%s", m_macAddress.c_str());
735   pwStr = StringUtils::Format("--password=%s",password.c_str());
736   portStr = StringUtils::Format("--server_port=%d",m_port);
737
738   if (!password.empty())
739   {
740     numArgs++;
741   }
742
743   char *argv[] = { "--apname=XBMC", (char*) portStr.c_str(), (char*) hwStr.c_str(), (char *)pwStr.c_str(), NULL };
744
745   if (m_pLibShairport->Load())
746   {
747
748     struct AudioOutput ao;
749     ao.ao_initialize = AudioOutputFunctions::ao_initialize;
750     ao.ao_play = AudioOutputFunctions::ao_play;
751     ao.ao_default_driver_id = AudioOutputFunctions::ao_default_driver_id;
752     ao.ao_open_live = AudioOutputFunctions::ao_open_live;
753     ao.ao_close = AudioOutputFunctions::ao_close;
754     ao.ao_append_option = AudioOutputFunctions::ao_append_option;
755     ao.ao_free_options = AudioOutputFunctions::ao_free_options;
756     ao.ao_get_option = AudioOutputFunctions::ao_get_option;
757 #ifdef HAVE_STRUCT_AUDIOOUTPUT_AO_SET_METADATA
758     ao.ao_set_metadata = AudioOutputFunctions::ao_set_metadata;    
759     ao.ao_set_metadata_coverart = AudioOutputFunctions::ao_set_metadata_coverart;        
760 #endif
761 #if defined(SHAIRPORT_AUDIOOUTPUT_VERSION)
762 #if   SHAIRPORT_AUDIOOUTPUT_VERSION >= 2
763     ao.ao_set_volume = AudioOutputFunctions::ao_set_volume;
764 #endif
765 #endif
766     struct printfPtr funcPtr;
767     funcPtr.extprintf = shairport_log;
768
769     m_pLibShairport->EnableDelayedUnload(false);
770     m_pLibShairport->shairport_set_ao(&ao);
771     m_pLibShairport->shairport_set_printf(&funcPtr);
772     m_pLibShairport->shairport_main(numArgs, argv);
773     ret = true;
774   }
775 #endif
776   return ret;
777 }
778
779 void CAirTunesServer::Deinitialize()
780 {
781 #if defined(HAVE_LIBSHAIRPLAY)
782   if (m_pLibShairplay && m_pLibShairplay->IsLoaded())
783   {
784     m_pLibShairplay->raop_stop(m_pRaop);
785     m_pLibShairplay->raop_destroy(m_pRaop);
786     m_pLibShairplay->Unload();
787   }
788 #else
789   if (m_pLibShairport && m_pLibShairport->IsLoaded())
790   {
791     m_pLibShairport->shairport_exit();
792     m_pLibShairport->Unload();
793   }
794 #endif
795 }
796
797 #endif
798