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