MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
mythraopconnection.cpp
Go to the documentation of this file.
1 #include <QTimer>
2 #include <QTcpSocket>
3 #include <QtEndian>
4 #include <QTextStream>
5 
6 #include "mythlogging.h"
7 #include "mythcorecontext.h"
8 #include "mythdirs.h"
9 #include "serverpool.h"
10 
11 #include "audiooutput.h"
12 #include "audiooutpututil.h"
13 
14 #include "mythraopdevice.h"
15 #include "mythraopconnection.h"
16 #include "mythairplayserver.h"
17 
18 #define LOC QString("RAOP Conn: ")
19 #define MAX_PACKET_SIZE 2048
20 
21 RSA *MythRAOPConnection::g_rsa = NULL;
23 
24 // RAOP RTP packet type
25 #define TIMING_REQUEST 0x52
26 #define TIMING_RESPONSE 0x53
27 #define SYNC 0x54
28 #define FIRSTSYNC (0x54 | 0x80)
29 #define RANGE_RESEND 0x55
30 #define AUDIO_RESEND 0x56
31 #define AUDIO_DATA 0x60
32 #define FIRSTAUDIO_DATA (0x60 | 0x80)
33 
34 
35 // Size (in ms) of audio buffered in audio card
36 #define AUDIOCARD_BUFFER 500
37 // How frequently we may call ProcessAudio (via QTimer)
38 // ideally 20ms, but according to documentation
39 // anything lower than 50ms on windows, isn't reliable
40 #define AUDIO_BUFFER 100
41 
42 class _NetStream : public QTextStream
43 {
44 public:
45  _NetStream(QIODevice *device) : QTextStream(device)
46  {
47  };
48  _NetStream &operator<<(const QString &str)
49  {
50  LOG(VB_GENERAL, LOG_DEBUG,
51  LOC + QString("Sending(%1): ").arg(str.length()) + str);
52  QTextStream *q = this;
53  *q << str;
54  return *this;
55  };
56 };
57 
58 MythRAOPConnection::MythRAOPConnection(QObject *parent, QTcpSocket *socket,
59  QByteArray id, int port)
60  : QObject(parent), m_watchdogTimer(NULL), m_socket(socket),
61  m_textStream(NULL), m_hardwareId(id),
62  m_incomingHeaders(), m_incomingContent(), m_incomingPartial(false),
63  m_incomingSize(0),
64  m_dataSocket(NULL), m_dataPort(port),
65  m_clientControlSocket(NULL), m_clientControlPort(0),
66  m_clientTimingSocket(NULL), m_clientTimingPort(0),
67  m_eventServer(NULL),
68  m_audio(NULL), m_codec(NULL), m_codeccontext(NULL),
69  m_channels(2), m_sampleSize(16), m_frameRate(44100),
70  m_framesPerPacket(352),m_dequeueAudioTimer(NULL),
71  m_queueLength(0), m_streamingStarted(false),
72  m_allowVolumeControl(true),
73  //audio sync
74  m_seqNum(0),
75  m_lastSequence(0), m_lastTimestamp(0),
76  m_currentTimestamp(0), m_nextSequence(0), m_nextTimestamp(0),
77  m_bufferLength(0), m_timeLastSync(0),
78  m_cardLatency(-1), m_adjustedLatency(-1), m_audioStarted(false),
79  // clock sync
80  m_masterTimeStamp(0), m_deviceTimeStamp(0), m_networkLatency(0),
81  m_clockSkew(0),
82  m_audioTimer(NULL),
83  m_progressStart(0), m_progressCurrent(0), m_progressEnd(0)
84 {
85 }
86 
88 {
89  CleanUp();
90 
91  foreach (QTcpSocket *client, m_eventClients)
92  {
93  client->close();
94  client->deleteLater();
95  }
96  m_eventClients.clear();
97 
98  if (m_eventServer)
99  {
100  m_eventServer->disconnect();
101  m_eventServer->close();
102  m_eventServer->deleteLater();
103  m_eventServer = NULL;
104  }
105 
106  // delete main socket
107  if (m_socket)
108  {
109  m_socket->disconnect();
110  m_socket->close();
111  m_socket->deleteLater();
112  m_socket = NULL;
113  }
114 
115  if (m_textStream)
116  {
117  delete m_textStream;
118  m_textStream = NULL;
119  }
120 }
121 
123 {
124  // delete audio timer
125  StopAudioTimer();
126 
127  // stop and delete watchdog timer
128  if (m_watchdogTimer)
129  {
130  m_watchdogTimer->stop();
131  delete m_watchdogTimer;
132  m_watchdogTimer = NULL;
133  }
134 
136  {
137  m_dequeueAudioTimer->stop();
138  delete m_dequeueAudioTimer;
139  m_dequeueAudioTimer = NULL;
140  }
141 
143  {
144  m_clientTimingSocket->disconnect();
146  delete m_clientTimingSocket;
147  m_clientTimingSocket = NULL;
148  }
149 
150  // delete data socket
151  if (m_dataSocket)
152  {
153  m_dataSocket->disconnect();
154  m_dataSocket->close();
155  m_dataSocket->deleteLater();
156  m_dataSocket = NULL;
157  }
158 
159  // client control socket
161  {
162  m_clientControlSocket->disconnect();
164  m_clientControlSocket->deleteLater();
165  m_clientControlSocket = NULL;
166  }
167 
168  // close audio decoder
169  DestroyDecoder();
170 
171  // free decoded audio buffer
172  ResetAudio();
173 
174  // close audio device
176 }
177 
179 {
180  // connect up the request socket
182  m_textStream->setCodec("UTF-8");
183  if (!connect(m_socket, SIGNAL(readyRead()), this, SLOT(readClient())))
184  {
185  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect client socket signal.");
186  return false;
187  }
188 
189  // create the data socket
190  m_dataSocket = new ServerPool();
191  if (!connect(m_dataSocket, SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)),
192  this, SLOT(udpDataReady(QByteArray, QHostAddress, quint16))))
193  {
194  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect data socket signal.");
195  return false;
196  }
197 
198  // try a few ports in case the first is in use
199  m_dataPort = m_dataSocket->tryBindingPort(m_dataPort, RAOP_PORT_RANGE);
200  if (m_dataPort < 0)
201  {
202  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to bind to a port for data.");
203  return false;
204  }
205 
206  LOG(VB_GENERAL, LOG_INFO, LOC +
207  QString("Bound to port %1 for incoming data").arg(m_dataPort));
208 
209  // load the private key
210  if (!LoadKey())
211  return false;
212 
213  // use internal volume control
214  m_allowVolumeControl = gCoreContext->GetNumSetting("MythControlsVolume", 1);
215 
216  // start the watchdog timer to auto delete the client after a period of inactivity
217  m_watchdogTimer = new QTimer();
218  connect(m_watchdogTimer, SIGNAL(timeout()), this, SLOT(timeout()));
219  m_watchdogTimer->start(10000);
220 
221  m_dequeueAudioTimer = new QTimer();
222  connect(m_dequeueAudioTimer, SIGNAL(timeout()), this, SLOT(ProcessAudio()));
223 
224  return true;
225 }
226 
231 void MythRAOPConnection::udpDataReady(QByteArray buf, QHostAddress peer,
232  quint16 port)
233 {
234  // restart the idle timer
235  if (m_watchdogTimer)
236  m_watchdogTimer->start(10000);
237 
238  if (!m_audio || !m_codec || !m_codeccontext)
239  return;
240 
241  uint8_t type;
242  uint16_t seq;
243  uint64_t timestamp;
244 
245  if (!GetPacketType(buf, type, seq, timestamp))
246  {
247  LOG(VB_GENERAL, LOG_DEBUG, LOC +
248  QString("Packet doesn't start with valid Rtp Header (0x%1)")
249  .arg((uint8_t)buf[0], 0, 16));
250  return;
251  }
252 
253  switch (type)
254  {
255  case SYNC:
256  case FIRSTSYNC:
257  ProcessSync(buf);
258  ProcessAudio();
259  return;
260 
261  case FIRSTAUDIO_DATA:
262  m_nextSequence = seq;
263  m_nextTimestamp = timestamp;
264  // With iTunes we know what the first sequence is going to be.
265  // iOS device do not tell us before streaming start what the first
266  // packet is going to be.
267  m_streamingStarted = true;
268  break;
269 
270  case AUDIO_DATA:
271  case AUDIO_RESEND:
272  break;
273 
274  case TIMING_RESPONSE:
275  ProcessTimeResponse(buf);
276  return;
277 
278  default:
279  LOG(VB_GENERAL, LOG_DEBUG, LOC +
280  QString("Packet type (0x%1) not handled")
281  .arg(type, 0, 16));
282  return;
283  }
284 
285  timestamp = framesToMs(timestamp);
286  if (timestamp < m_currentTimestamp)
287  {
288  LOG(VB_GENERAL, LOG_DEBUG, LOC +
289  QString("Received packet %1 too late, ignoring")
290  .arg(seq));
291  return;
292  }
293  // regular data packet
294  if (type == AUDIO_DATA || type == FIRSTAUDIO_DATA)
295  {
296  if (m_streamingStarted && seq != m_nextSequence)
297  SendResendRequest(timestamp, m_nextSequence, seq);
298 
299  m_nextSequence = seq + 1;
300  m_nextTimestamp = timestamp;
301  m_streamingStarted = true;
302  }
303 
304  if (!m_streamingStarted)
305  return;
306 
307  // resent packet
308  if (type == AUDIO_RESEND)
309  {
310  if (m_resends.contains(seq))
311  {
312  LOG(VB_GENERAL, LOG_DEBUG, LOC +
313  QString("Received required resend %1 (with ts:%2 last:%3)")
314  .arg(seq).arg(timestamp).arg(m_nextSequence));
315  m_resends.remove(seq);
316  }
317  else
318  LOG(VB_GENERAL, LOG_WARNING, LOC +
319  QString("Received unexpected resent packet %1")
320  .arg(seq));
321  }
322 
323  // Check that the audio packet is valid, do so by decoding it. If an error
324  // occurs, ask to resend it
325  QList<AudioData> *decoded = new QList<AudioData>();
326  int numframes = decodeAudioPacket(type, &buf, decoded);
327  if (numframes < 0)
328  {
329  // an error occurred, ask for the audio packet once again.
330  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Error decoding audio"));
331  SendResendRequest(timestamp, seq, seq+1);
332  return;
333  }
335  frames.seq = seq;
336  frames.data = decoded;
337  m_audioQueue.insert(timestamp, frames);
338  ProcessAudio();
339 }
340 
341 void MythRAOPConnection::ProcessSync(const QByteArray &buf)
342 {
343  bool first = (uint8_t)buf[0] == 0x90; // First sync is 0x90,0x55
344  const char *req = buf.constData();
345  uint64_t current_ts = qFromBigEndian(*(uint32_t *)(req + 4));
346  uint64_t next_ts = qFromBigEndian(*(uint32_t *)(req + 16));
347 
348  uint64_t current = framesToMs(current_ts);
349  uint64_t next = framesToMs(next_ts);
350 
354 
355  if (first)
356  {
357  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Receiving first SYNC packet"));
358  }
359  else
360  {
361  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Receiving SYNC packet"));
362  }
363 
364  timeval t; gettimeofday(&t, NULL);
365  m_timeLastSync = t.tv_sec * 1000 + t.tv_usec / 1000;
366 
367  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("SYNC: cur:%1 next:%2 time:%3")
368  .arg(m_currentTimestamp).arg(m_nextTimestamp).arg(m_timeLastSync));
369 
370  int64_t delay = framesToMs(m_audioQueue.size() * m_framesPerPacket);
371  int64_t audiots = m_audio->GetAudiotime();
372  int64_t currentLatency = 0LL;
373 
374  if (m_audioStarted)
375  {
376  currentLatency = (int64_t)audiots - (int64_t)m_currentTimestamp;
377  }
378 
379  LOG(VB_GENERAL, LOG_DEBUG, LOC +
380  QString("RAOP timestamps: about to play:%1 desired:%2 latency:%3")
381  .arg(audiots).arg(m_currentTimestamp)
382  .arg(currentLatency));
383 
384  delay += m_audio->GetAudioBufferedTime();
385  delay += currentLatency;
386 
387  LOG(VB_GENERAL, LOG_DEBUG, LOC +
388  QString("Queue=%1 buffer=%2ms ideal=%3ms diffts:%4ms")
389  .arg(m_audioQueue.size())
390  .arg(delay)
391  .arg(m_bufferLength)
392  .arg(m_bufferLength-delay));
393 
394  if (m_adjustedLatency <= 0 && m_audioStarted &&
395  (-currentLatency > AUDIOCARD_BUFFER))
396  {
397  // Too much delay in playback.
398  // The threshold is a value chosen to be loose enough so it doesn't
399  // trigger too often, but should be low enough to detect any accidental
400  // interruptions.
401  // Will drop some frames in next ProcessAudio
402  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
403  QString("Too much delay (%1ms), adjusting")
404  .arg(m_bufferLength - delay));
405 
407 
408  // Expire old audio
409  ExpireResendRequests(m_currentTimestamp - m_adjustedLatency);
410  int res = ExpireAudio(m_currentTimestamp - m_adjustedLatency);
411  if (res > 0)
412  {
413  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Drop %1 packets").arg(res));
414  }
415 
416  m_audioStarted = false;
417  }
418 }
419 
425  uint16_t expected, uint16_t got)
426 {
428  return;
429 
430  int16_t missed = (got < expected) ?
431  (int16_t)(((int32_t)got + UINT16_MAX + 1) - expected) :
432  got - expected;
433 
434  LOG(VB_GENERAL, LOG_INFO, LOC +
435  QString("Missed %1 packet(s): expected %2 got %3 ts:%4")
436  .arg(missed).arg(expected).arg(got).arg(timestamp));
437 
438  char req[8];
439  req[0] = 0x80;
440  req[1] = RANGE_RESEND | 0x80;
441  *(uint16_t *)(req + 2) = qToBigEndian(m_seqNum++);
442  *(uint16_t *)(req + 4) = qToBigEndian(expected); // missed seqnum
443  *(uint16_t *)(req + 6) = qToBigEndian(missed); // count
444 
445  if (m_clientControlSocket->writeDatagram(req, sizeof(req),
447  == sizeof(req))
448  {
449  for (uint16_t count = 0; count < missed; count++)
450  {
451  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Sent resend for %1")
452  .arg(expected + count));
453  m_resends.insert(expected + count, timestamp);
454  }
455  }
456  else
457  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to send resend request.");
458 }
459 
466 {
467  if (m_resends.isEmpty())
468  return;
469 
470  QMutableMapIterator<uint16_t,uint64_t> it(m_resends);
471  while (it.hasNext())
472  {
473  it.next();
474  if (it.value() < timestamp && m_streamingStarted)
475  {
476  LOG(VB_GENERAL, LOG_WARNING, LOC +
477  QString("Never received resend packet %1").arg(it.key()));
478  m_resends.remove(it.key());
479  }
480  }
481 }
482 
488 {
489  if (!m_clientControlSocket) // should never happen
490  return;
491 
492  timeval t;
493  gettimeofday(&t, NULL);
494 
495  char req[32];
496  req[0] = 0x80;
497  req[1] = TIMING_REQUEST | 0x80;
498  // this is always 0x00 0x07 according to http://blog.technologeek.org/airtunes-v2
499  // no other value works
500  req[2] = 0x00;
501  req[3] = 0x07;
502  *(uint32_t *)(req + 4) = (uint32_t)0;
503  *(uint64_t *)(req + 8) = (uint64_t)0;
504  *(uint64_t *)(req + 16) = (uint64_t)0;
505  *(uint32_t *)(req + 24) = qToBigEndian((uint32_t)t.tv_sec);
506  *(uint32_t *)(req + 28) = qToBigEndian((uint32_t)t.tv_usec);
507 
508  if (m_clientTimingSocket->writeDatagram(req, sizeof(req), m_peerAddress, m_clientTimingPort) != sizeof(req))
509  {
510  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to send resend time request.");
511  return;
512  }
513  LOG(VB_GENERAL, LOG_DEBUG, LOC +
514  QString("Requesting master time (Local %1.%2)")
515  .arg(t.tv_sec).arg(t.tv_usec));
516 }
517 
525 {
526  timeval t1, t2;
527  const char *req = buf.constData();
528 
529  t1.tv_sec = qFromBigEndian(*(uint32_t *)(req + 8));
530  t1.tv_usec = qFromBigEndian(*(uint32_t *)(req + 12));
531 
532  gettimeofday(&t2, NULL);
533  uint64_t time1, time2;
534  time1 = t1.tv_sec * 1000 + t1.tv_usec / 1000;
535  time2 = t2.tv_sec * 1000 + t2.tv_usec / 1000;
536  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Read back time (Local %1.%2)")
537  .arg(t1.tv_sec).arg(t1.tv_usec));
538  // network latency equal time difference in ms between request and response
539  // divide by two for approximate time of one way trip
540  m_networkLatency = (time2 - time1) / 2;
541  LOG(VB_AUDIO, LOG_DEBUG, LOC + QString("Network Latency: %1ms")
542  .arg(m_networkLatency));
543 
544  // now calculate the time difference between the client and us.
545  // this is NTP time, where sec is in seconds, and ticks is in 1/2^32s
546  uint32_t sec = qFromBigEndian(*(uint32_t *)(req + 24));
547  uint32_t ticks = qFromBigEndian(*(uint32_t *)(req + 28));
548  // convert ticks into ms
549  int64_t master = NTPToLocal(sec, ticks);
550  m_clockSkew = master - time2;
551 }
552 
553 uint64_t MythRAOPConnection::NTPToLocal(uint32_t sec, uint32_t ticks)
554 {
555  return (int64_t)sec * 1000LL + (((int64_t)ticks * 1000LL) >> 32);
556 }
557 
558 bool MythRAOPConnection::GetPacketType(const QByteArray &buf, uint8_t &type,
559  uint16_t &seq, uint64_t &timestamp)
560 {
561  // All RAOP packets start with | 0x80/0x90 (first sync) | PACKET_TYPE |
562  if ((uint8_t)buf[0] != 0x80 && (uint8_t)buf[0] != 0x90)
563  {
564  return false;
565  }
566 
567  type = (char)buf[1];
568  // Is it first sync packet?
569  if ((uint8_t)buf[0] == 0x90 && type == FIRSTSYNC)
570  {
571  return true;
572  }
573  if (type != FIRSTAUDIO_DATA)
574  {
575  type &= ~0x80;
576  }
577 
578  if (type != AUDIO_DATA && type != FIRSTAUDIO_DATA && type != AUDIO_RESEND)
579  return true;
580 
581  const char *ptr = buf.constData();
582  if (type == AUDIO_RESEND)
583  {
584  ptr += 4;
585  }
586  seq = qFromBigEndian(*(uint16_t *)(ptr + 2));
587  timestamp = qFromBigEndian(*(uint32_t *)(ptr + 4));
588  return true;
589 }
590 
591 // Audio decode / playback related routines
592 
594  const QByteArray *buf,
595  QList<AudioData> *dest)
596 {
597  const char *data_in = buf->constData();
598  int len = buf->size();
599  if (type == AUDIO_RESEND)
600  {
601  data_in += 4;
602  len -= 4;
603  }
604  data_in += 12;
605  len -= 12;
606  if (len < 16)
607  return -1;
608 
609  int aeslen = len & ~0xf;
610  unsigned char iv[16];
611  unsigned char decrypted_data[MAX_PACKET_SIZE];
612  memcpy(iv, m_AESIV.constData(), sizeof(iv));
613  AES_cbc_encrypt((const unsigned char *)data_in,
614  decrypted_data, aeslen,
615  &m_aesKey, iv, AES_DECRYPT);
616  memcpy(decrypted_data + aeslen, data_in + aeslen, len - aeslen);
617 
618  AVPacket tmp_pkt;
619  AVCodecContext *ctx = m_codeccontext;
620 
621  av_init_packet(&tmp_pkt);
622  tmp_pkt.data = decrypted_data;
623  tmp_pkt.size = len;
624 
625  uint32_t frames_added = 0;
626  uint8_t *samples = (uint8_t *)av_mallocz(AVCODEC_MAX_AUDIO_FRAME_SIZE);
627  while (tmp_pkt.size > 0)
628  {
629  int data_size;
630  int ret = AudioOutputUtil::DecodeAudio(ctx, samples,
631  data_size, &tmp_pkt);
632  if (ret < 0)
633  {
634  av_free(samples);
635  return -1;
636  }
637 
638  if (data_size)
639  {
640  int num_samples = data_size /
641  (ctx->channels * av_get_bytes_per_sample(ctx->sample_fmt));
642 
643  frames_added += num_samples;
644  AudioData block;
645  block.data = samples;
646  block.length = data_size;
647  block.frames = num_samples;
648  dest->append(block);
649  }
650  tmp_pkt.data += ret;
651  tmp_pkt.size -= ret;
652  }
653  return frames_added;
654 }
655 
657 {
658  if (!m_streamingStarted || !m_audio)
659  return;
660 
661  if (m_audio->IsPaused())
662  {
663  // ALSA takes a while to unpause, enough to have SYNC starting to drop
664  // packets, so unpause as early as possible
665  m_audio->Pause(false);
666  }
667  timeval t; gettimeofday(&t, NULL);
668  uint64_t dtime = (t.tv_sec * 1000 + t.tv_usec / 1000) - m_timeLastSync;
669  uint64_t rtp = dtime + m_currentTimestamp;
670  uint64_t buffered = m_audioStarted ? m_audio->GetAudioBufferedTime() : 0;
671 
672  // Keep audio framework buffer as short as possible, keeping everything in
673  // m_audioQueue, so we can easily reset the least amount possible
674  if (buffered > AUDIOCARD_BUFFER)
675  return;
676 
677  // Also make sure m_audioQueue never goes to less than 1/3 of the RDP stream
678  // total latency, this should gives us enough time to receive missed packets
679  int64_t queue = framesToMs(m_audioQueue.size() * m_framesPerPacket);
680  if (queue < m_bufferLength / 3)
681  return;
682 
683  rtp += buffered;
684 
685  // How many packets to add to the audio card, to fill AUDIOCARD_BUFFER
686  int max_packets = ((AUDIOCARD_BUFFER - buffered)
687  * m_frameRate / 1000) / m_framesPerPacket;
688  int i = 0;
689  uint64_t timestamp = 0;
690 
691  QMapIterator<uint64_t,AudioPacket> packet_it(m_audioQueue);
692  while (packet_it.hasNext() && i <= max_packets)
693  {
694  packet_it.next();
695 
696  timestamp = packet_it.key();
697  if (timestamp < rtp)
698  {
699  if (!m_audioStarted)
700  {
701  m_audio->Reset(); // clear audio card
702  }
703  AudioPacket frames = packet_it.value();
704 
705  if (m_lastSequence != frames.seq)
706  {
707  LOG(VB_GENERAL, LOG_ERR, LOC +
708  QString("Audio discontinuity seen. Played %1 (%3) expected %2")
709  .arg(frames.seq).arg(m_lastSequence).arg(timestamp));
710  m_lastSequence = frames.seq;
711  }
712  m_lastSequence++;
713 
714  QList<AudioData>::iterator it = frames.data->begin();
715  for (; it != frames.data->end(); ++it)
716  {
717  AudioData *data = &(*it);
718  int offset = 0;
719  int frames = 0;
720 
721  if (m_adjustedLatency > 0)
722  {
723  // calculate how many frames we have to drop to catch up
724  offset = (m_adjustedLatency * m_frameRate / 1000) *
726  if (offset > data->length)
727  offset = data->length;
728  frames = offset / m_audio->GetBytesPerFrame();
729  m_adjustedLatency -= framesToMs(frames+1);
730  LOG(VB_PLAYBACK, LOG_DEBUG, LOC +
731  QString("ProcessAudio: Dropping %1 frames to catch up "
732  "(%2ms to go)")
733  .arg(frames).arg(m_adjustedLatency));
734  timestamp += framesToMs(frames);
735  }
736  m_audio->AddData((char *)data->data + offset,
737  data->length - offset,
738  timestamp, frames);
739  timestamp += m_audio->LengthLastData();
740  }
741  i++;
742  m_audioStarted = true;
743  }
744  else // QMap is sorted, so no need to continue if not found
745  break;
746  }
747 
748  ExpireAudio(timestamp);
749  m_lastTimestamp = timestamp;
750 
751  // restart audio timer should we stop receiving data on regular interval,
752  // we need to continue processing the audio queue
753  m_dequeueAudioTimer->start(AUDIO_BUFFER);
754 }
755 
756 int MythRAOPConnection::ExpireAudio(uint64_t timestamp)
757 {
758  int res = 0;
759  QMutableMapIterator<uint64_t,AudioPacket> packet_it(m_audioQueue);
760  while (packet_it.hasNext())
761  {
762  packet_it.next();
763  if (packet_it.key() < timestamp)
764  {
765  AudioPacket frames = packet_it.value();
766  if (frames.data)
767  {
768  QList<AudioData>::iterator it = frames.data->begin();
769  for (; it != frames.data->end(); ++it)
770  {
771  av_free(it->data);
772  }
773  delete frames.data;
774  }
775  m_audioQueue.remove(packet_it.key());
776  res++;
777  }
778  }
779  return res;
780 }
781 
783 {
784  if (m_audio)
785  {
786  m_audio->Reset();
787  }
788  ExpireAudio(UINT64_MAX);
789  ExpireResendRequests(UINT64_MAX);
790  m_audioStarted = false;
791 }
792 
794 {
795  LOG(VB_GENERAL, LOG_INFO, LOC + "Closing connection after inactivity.");
796  m_socket->disconnectFromHost();
797 }
798 
800 {
801  if (!m_audio && OpenAudioDevice())
802  {
803  CreateDecoder();
804  }
805 
806  if (m_audio && m_codec && m_codeccontext)
807  {
808  StopAudioTimer();
809  }
810 }
811 
817 {
818  QTcpSocket *socket = (QTcpSocket *)sender();
819  if (!socket)
820  return;
821 
822  QByteArray data = socket->readAll();
823  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("readClient(%1): ")
824  .arg(data.size()) + data.constData());
825 
826  // For big content, we may be called several times for a single packet
827  if (!m_incomingPartial)
828  {
829  m_incomingHeaders.clear();
830  m_incomingContent.clear();
831  m_incomingSize = 0;
832 
833  QTextStream stream(data);
834  QString line;
835  do
836  {
837  line = stream.readLine();
838  if (line.size() == 0)
839  break;
840  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Header(%1) = %2")
841  .arg(m_socket->peerAddress().toString())
842  .arg(line));
843  m_incomingHeaders.append(line);
844  if (line.contains("Content-Length:"))
845  {
846  m_incomingSize = line.mid(line.indexOf(" ") + 1).toInt();
847  }
848  }
849  while (!line.isNull());
850 
851  if (m_incomingHeaders.size() == 0)
852  return;
853 
854  if (!stream.atEnd())
855  {
856  int pos = stream.pos();
857  if (pos > 0)
858  {
859  m_incomingContent.append(data.mid(pos));
860  }
861  }
862  }
863  else
864  {
865  m_incomingContent.append(data);
866  }
867 
868  // If we haven't received all the content yet, wait (see when receiving
869  // coverart
870  if (m_incomingContent.size() < m_incomingSize)
871  {
872  m_incomingPartial = true;
873  return;
874  }
875  else
876  {
877  m_incomingPartial = false;
878  }
879  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Content(%1) = %2")
880  .arg(m_incomingContent.size()).arg(m_incomingContent.constData()));
881 
883 }
884 
885 void MythRAOPConnection::ProcessRequest(const QStringList &header,
886  const QByteArray &content)
887 {
888  if (header.isEmpty())
889  return;
890 
891  RawHash tags = FindTags(header);
892 
893  if (!tags.contains("CSeq"))
894  {
895  LOG(VB_GENERAL, LOG_ERR, LOC + "ProcessRequest: Didn't find CSeq");
896  return;
897  }
898 
899  QString option = header[0].left(header[0].indexOf(" "));
900 
901  // process RTP-info field
902  bool gotRTP = false;
903  uint16_t RTPseq;
904  uint64_t RTPtimestamp;
905  if (tags.contains("RTP-Info"))
906  {
907  gotRTP = true;
908  QString data = tags["RTP-Info"];
909  QStringList items = data.split(";");
910  foreach (QString item, items)
911  {
912  if (item.startsWith("seq"))
913  {
914  RTPseq = item.mid(item.indexOf("=") + 1).trimmed().toUShort();
915  }
916  else if (item.startsWith("rtptime"))
917  {
918  RTPtimestamp = item.mid(item.indexOf("=") + 1).trimmed().toUInt();
919  }
920  }
921  LOG(VB_GENERAL, LOG_INFO, LOC + QString("RTP-Info: seq=%1 rtptime=%2")
922  .arg(RTPseq).arg(RTPtimestamp));
923  }
924 
925  if (gCoreContext->GetNumSetting("AirPlayPasswordEnabled", false))
926  {
927  if (m_nonce.isEmpty())
928  {
930  }
931  if (!tags.contains("Authorization"))
932  {
933  // 60 seconds to enter password.
934  m_watchdogTimer->start(60000);
936  return;
937  }
938 
939  QByteArray auth;
940  if (DigestMd5Response(tags["Authorization"], option, m_nonce,
941  gCoreContext->GetSetting("AirPlayPassword"),
942  auth) == auth)
943  {
944  LOG(VB_GENERAL, LOG_INFO, LOC + "RAOP client authenticated");
945  }
946  else
947  {
948  LOG(VB_GENERAL, LOG_INFO, LOC + "RAOP authentication failed");
950  return;
951  }
952  }
953  *m_textStream << "RTSP/1.0 200 OK\r\n";
954 
955  if (tags.contains("Apple-Challenge"))
956  {
957  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Received Apple-Challenge"));
958 
959  *m_textStream << "Apple-Response: ";
960  if (!LoadKey())
961  return;
962  int tosize = RSA_size(LoadKey());
963  uint8_t *to = new uint8_t[tosize];
964 
965  QByteArray challenge =
966  QByteArray::fromBase64(tags["Apple-Challenge"].toLatin1());
967  int challenge_size = challenge.size();
968  if (challenge_size != 16)
969  {
970  LOG(VB_GENERAL, LOG_ERR, LOC +
971  QString("Decoded challenge size %1, expected 16")
972  .arg(challenge_size));
973  if (challenge_size > 16)
974  challenge_size = 16;
975  }
976 
977  int i = 0;
978  unsigned char from[38];
979  memcpy(from, challenge.constData(), challenge_size);
980  i += challenge_size;
981  if (m_socket->localAddress().protocol() ==
982  QAbstractSocket::IPv4Protocol)
983  {
984  uint32_t ip = m_socket->localAddress().toIPv4Address();
985  ip = qToBigEndian(ip);
986  memcpy(from + i, &ip, 4);
987  i += 4;
988  }
989  else if (m_socket->localAddress().protocol() ==
990  QAbstractSocket::IPv6Protocol)
991  {
992  Q_IPV6ADDR ip = m_socket->localAddress().toIPv6Address();
993  if(memcmp(&ip,
994  "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\xff\xff",
995  12) == 0)
996  {
997  memcpy(from + i, &ip[12], 4);
998  i += 4;
999  }
1000  else
1001  {
1002  memcpy(from + i, &ip, 16);
1003  i += 16;
1004  }
1005  }
1006  memcpy(from + i, m_hardwareId.constData(), AIRPLAY_HARDWARE_ID_SIZE);
1007  i += AIRPLAY_HARDWARE_ID_SIZE;
1008 
1009  int pad = 32 - i;
1010  if (pad > 0)
1011  {
1012  memset(from + i, 0, pad);
1013  i += pad;
1014  }
1015 
1016  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1017  QString("Full base64 response: '%1' size %2")
1018  .arg(QByteArray((const char *)from, i).toBase64().constData())
1019  .arg(i));
1020 
1021  RSA_private_encrypt(i, from, to, LoadKey(), RSA_PKCS1_PADDING);
1022 
1023  QByteArray base64 = QByteArray((const char *)to, tosize).toBase64();
1024  delete[] to;
1025 
1026  for (int pos = base64.size() - 1; pos > 0; pos--)
1027  {
1028  if (base64[pos] == '=')
1029  base64[pos] = ' ';
1030  else
1031  break;
1032  }
1033  LOG(VB_GENERAL, LOG_DEBUG, QString("tSize=%1 tLen=%2 tResponse=%3")
1034  .arg(tosize).arg(base64.size()).arg(base64.constData()));
1035  *m_textStream << base64.trimmed() << "\r\n";
1036  }
1037 
1038  if (option == "OPTIONS")
1039  {
1040  *m_textStream << "Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, "
1041  "TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER, POST, GET\r\n";
1042  }
1043  else if (option == "ANNOUNCE")
1044  {
1045  QStringList lines = splitLines(content);
1046  foreach (QString line, lines)
1047  {
1048  if (line.startsWith("a=rsaaeskey:"))
1049  {
1050  QString key = line.mid(12).trimmed();
1051  QByteArray decodedkey = QByteArray::fromBase64(key.toLatin1());
1052  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1053  QString("RSAAESKey: %1 (decoded size %2)")
1054  .arg(key).arg(decodedkey.size()));
1055 
1056  if (LoadKey())
1057  {
1058  int size = sizeof(char) * RSA_size(LoadKey());
1059  char *decryptedkey = new char[size];
1060  if (RSA_private_decrypt(decodedkey.size(),
1061  (const unsigned char *)decodedkey.constData(),
1062  (unsigned char *)decryptedkey,
1063  LoadKey(), RSA_PKCS1_OAEP_PADDING))
1064  {
1065  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1066  "Successfully decrypted AES key from RSA.");
1067  AES_set_decrypt_key((const unsigned char *)decryptedkey,
1068  128, &m_aesKey);
1069  }
1070  else
1071  {
1072  LOG(VB_GENERAL, LOG_WARNING, LOC +
1073  "Failed to decrypt AES key from RSA.");
1074  }
1075  delete [] decryptedkey;
1076  }
1077  }
1078  else if (line.startsWith("a=aesiv:"))
1079  {
1080  QString aesiv = line.mid(8).trimmed();
1081  m_AESIV = QByteArray::fromBase64(aesiv.toLatin1());
1082  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1083  QString("AESIV: %1 (decoded size %2)")
1084  .arg(aesiv).arg(m_AESIV.size()));
1085  }
1086  else if (line.startsWith("a=fmtp:"))
1087  {
1088  m_audioFormat.clear();
1089  QString format = line.mid(7).trimmed();
1090  QList<QString> fmts = format.split(' ');
1091  foreach (QString fmt, fmts)
1092  m_audioFormat.append(fmt.toInt());
1093 
1094  foreach (int fmt, m_audioFormat)
1095  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1096  QString("Audio parameter: %1").arg(fmt));
1100  m_frameRate = m_audioFormat[11];
1101  }
1102  }
1103  }
1104  else if (option == "SETUP")
1105  {
1106  if (tags.contains("Transport"))
1107  {
1108  // New client is trying to play audio, disconnect all the other clients
1109  ((MythRAOPDevice*)parent())->DeleteAllClients(this);
1110  gCoreContext->WantingPlayback(parent());
1111 
1112  int control_port = 0;
1113  int timing_port = 0;
1114  QString data = tags["Transport"];
1115  QStringList items = data.split(";");
1116  bool events = false;
1117 
1118  foreach (QString item, items)
1119  {
1120  if (item.startsWith("control_port"))
1121  control_port = item.mid(item.indexOf("=") + 1).trimmed().toUInt();
1122  else if (item.startsWith("timing_port"))
1123  timing_port = item.mid(item.indexOf("=") + 1).trimmed().toUInt();
1124  else if (item.startsWith("events"))
1125  events = true;
1126  }
1127 
1128  LOG(VB_GENERAL, LOG_INFO, LOC +
1129  QString("Negotiated setup with client %1 on port %2")
1130  .arg(m_socket->peerAddress().toString())
1131  .arg(m_socket->peerPort()));
1132  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1133  QString("control port: %1 timing port: %2")
1134  .arg(control_port).arg(timing_port));
1135 
1136  m_peerAddress = m_socket->peerAddress();
1137 
1139  {
1140  m_clientControlSocket->disconnect();
1142  delete m_clientControlSocket;
1143  }
1144 
1145  m_clientControlSocket = new ServerPool(this);
1146  int controlbind_port =
1147  m_clientControlSocket->tryBindingPort(control_port,
1148  RAOP_PORT_RANGE);
1149  if (controlbind_port < 0)
1150  {
1151  LOG(VB_GENERAL, LOG_ERR, LOC +
1152  QString("Failed to bind to client control port. "
1153  "Control of audio stream may fail"));
1154  }
1155  else
1156  {
1157  LOG(VB_GENERAL, LOG_INFO, LOC +
1158  QString("Bound to client control port %1 on port %2")
1159  .arg(control_port).arg(controlbind_port));
1160  }
1161  m_clientControlPort = control_port;
1162  connect(m_clientControlSocket,
1163  SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)),
1164  this,
1165  SLOT(udpDataReady(QByteArray, QHostAddress, quint16)));
1166 
1168  {
1169  m_clientTimingSocket->disconnect();
1171  delete m_clientTimingSocket;
1172  }
1173 
1174  m_clientTimingSocket = new ServerPool(this);
1175  int timingbind_port =
1176  m_clientTimingSocket->tryBindingPort(timing_port,
1177  RAOP_PORT_RANGE);
1178  if (timingbind_port < 0)
1179  {
1180  LOG(VB_GENERAL, LOG_ERR, LOC +
1181  QString("Failed to bind to client timing port. "
1182  "Timing of audio stream will be incorrect"));
1183  }
1184  else
1185  {
1186  LOG(VB_GENERAL, LOG_INFO, LOC +
1187  QString("Bound to client timing port %1 on port %2")
1188  .arg(timing_port).arg(timingbind_port));
1189  }
1190  m_clientTimingPort = timing_port;
1191  connect(m_clientTimingSocket,
1192  SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)),
1193  this,
1194  SLOT(udpDataReady(QByteArray, QHostAddress, quint16)));
1195 
1196  if (m_eventServer)
1197  {
1198  // Should never get here, but just in case
1199  QTcpSocket *client;
1200  foreach (client, m_eventClients)
1201  {
1202  client->disconnect();
1203  client->abort();
1204  delete client;
1205  }
1206  m_eventClients.clear();
1207  m_eventServer->disconnect();
1208  m_eventServer->close();
1209  delete m_eventServer;
1210  m_eventServer = NULL;
1211  }
1212 
1213  if (events)
1214  {
1215  m_eventServer = new ServerPool(this);
1217  RAOP_PORT_RANGE);
1218  if (m_eventPort < 0)
1219  {
1220  LOG(VB_GENERAL, LOG_ERR, LOC +
1221  "Failed to find a port for RAOP events server.");
1222  }
1223  else
1224  {
1225  LOG(VB_GENERAL, LOG_INFO, LOC +
1226  QString("Listening for RAOP events on port %1").arg(m_eventPort));
1227  connect(m_eventServer, SIGNAL(newConnection(QTcpSocket *)),
1228  this, SLOT(newEventClient(QTcpSocket *)));
1229  }
1230  }
1231 
1232  if (OpenAudioDevice())
1233  {
1234  CreateDecoder();
1235  // Calculate audio card latency
1236  if (m_cardLatency < 0)
1237  {
1239  // if audio isn't started, start playing 500ms worth of silence
1240  // and measure timestamp difference
1241  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1242  QString("Audio hardware latency: %1ms")
1244  }
1245  }
1246 
1247  // Recreate transport line with new ports value
1248  QString newdata;
1249  bool first = true;
1250  foreach (QString item, items)
1251  {
1252  if (!first)
1253  {
1254  newdata += ";";
1255  }
1256  if (item.startsWith("control_port"))
1257  {
1258  newdata += "control_port=" + QString::number(controlbind_port);
1259  }
1260  else if (item.startsWith("timing_port"))
1261  {
1262  newdata += "timing_port=" + QString::number(timingbind_port);
1263  }
1264  else if (item.startsWith("events"))
1265  {
1266  newdata += "event_port=" + QString::number(m_eventPort);
1267  }
1268  else
1269  {
1270  newdata += item;
1271  }
1272  first = false;
1273  }
1274  if (!first)
1275  {
1276  newdata += ";";
1277  }
1278  newdata += "server_port=" + QString::number(m_dataPort);
1279 
1280  *m_textStream << "Transport: " << newdata << "\r\n";
1281  *m_textStream << "Session: 1\r\n";
1282  *m_textStream << "Audio-Jack-Status: connected\r\n";
1283 
1284  // Ask for master clock value to determine time skew and average network latency
1285  SendTimeRequest();
1286  }
1287  else
1288  {
1289  LOG(VB_GENERAL, LOG_ERR, LOC +
1290  "No Transport details found - Ignoring");
1291  }
1292  }
1293  else if (option == "RECORD")
1294  {
1295  if (gotRTP)
1296  {
1297  m_nextSequence = RTPseq;
1298  m_nextTimestamp = RTPtimestamp;
1299  }
1300 
1301  // Calculate audio card latency
1302  if (m_cardLatency > 0)
1303  {
1304  *m_textStream << QString("Audio-Latency: %1")
1306  }
1307  }
1308  else if (option == "FLUSH")
1309  {
1310  if (gotRTP)
1311  {
1312  m_nextSequence = RTPseq;
1313  m_nextTimestamp = RTPtimestamp;
1315  }
1316  // determine RTP timestamp of last sample played
1317  uint64_t timestamp = m_audioStarted && m_audio ?
1319  *m_textStream << "RTP-Info: rtptime=" << QString::number(timestamp);
1320  m_streamingStarted = false;
1321  ResetAudio();
1322  }
1323  else if (option == "SET_PARAMETER")
1324  {
1325  if (tags.contains("Content-Type"))
1326  {
1327  if (tags["Content-Type"] == "text/parameters")
1328  {
1329  QString name = content.left(content.indexOf(":"));
1330  QString param = content.mid(content.indexOf(":") + 1).trimmed();
1331 
1332  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1333  QString("text/parameters: name=%1 parem=%2")
1334  .arg(name).arg(param));
1335 
1336  if (name == "volume" && m_allowVolumeControl && m_audio)
1337  {
1338  float volume = (param.toFloat() + 30.0f) * 100.0f / 30.0f;
1339  if (volume < 0.01f)
1340  volume = 0.0f;
1341  LOG(VB_GENERAL, LOG_INFO,
1342  LOC + QString("Setting volume to %1 (raw %3)")
1343  .arg(volume).arg(param));
1344  m_audio->SetCurrentVolume((int)volume);
1345  }
1346  else if (name == "progress")
1347  {
1348  QStringList items = param.split("/");
1349  if (items.size() == 3)
1350  {
1351  m_progressStart = items[0].toUInt();
1352  m_progressCurrent = items[1].toUInt();
1353  m_progressEnd = items[2].toUInt();
1354  }
1355  int length =
1357  int current =
1359 
1360  LOG(VB_GENERAL, LOG_INFO,
1361  LOC +QString("Progress: %1/%2")
1362  .arg(stringFromSeconds(current))
1363  .arg(stringFromSeconds(length)));
1364  }
1365  }
1366  else if(tags["Content-Type"] == "image/jpeg")
1367  {
1368  // Receiving image coverart
1369  m_artwork = content;
1370  }
1371  else if (tags["Content-Type"] == "application/x-dmap-tagged")
1372  {
1373  // Receiving DMAP metadata
1374  QMap<QString,QString> map = decodeDMAP(content);
1375  LOG(VB_GENERAL, LOG_INFO,
1376  QString("Receiving Title:%1 Artist:%2 Album:%3 Format:%4")
1377  .arg(map["minm"]).arg(map["asar"])
1378  .arg(map["asal"]).arg(map["asfm"]));
1379  }
1380  }
1381  }
1382  else if (option == "TEARDOWN")
1383  {
1384  m_socket->disconnectFromHost();
1385  return;
1386  }
1387  else
1388  {
1389  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Command not handled: %1")
1390  .arg(option));
1391  }
1392  FinishResponse(m_textStream, m_socket, option, tags["CSeq"]);
1393 }
1394 
1396  QTcpSocket *socket,
1397  QString &cseq)
1398 {
1399  if (!stream)
1400  return;
1401  *stream << "RTSP/1.0 401 Unauthorised\r\n";
1402  *stream << "Server: AirTunes/130.14\r\n";
1403  *stream << "WWW-Authenticate: Digest realm=\"raop\", ";
1404  *stream << "nonce=\"" + m_nonce + "\"\r\n";
1405  *stream << "CSeq: " << cseq << "\r\n";
1406  *stream << "\r\n";
1407  stream->flush();
1408  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1409  QString("Finished Authentication request %2, Send: %3")
1410  .arg(cseq).arg(socket->flush()));
1411 }
1412 
1413 void MythRAOPConnection::FinishResponse(_NetStream *stream, QTcpSocket *socket,
1414  QString &option, QString &cseq)
1415 {
1416  if (!stream)
1417  return;
1418  *stream << "Server: AirTunes/130.14\r\n";
1419  *stream << "CSeq: " << cseq << "\r\n";
1420  *stream << "\r\n";
1421  stream->flush();
1422  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Finished %1 %2 , Send: %3")
1423  .arg(option).arg(cseq).arg(socket->flush()));
1424 }
1425 
1432 {
1433  static QMutex lock;
1434  QMutexLocker locker(&lock);
1435 
1436  if (g_rsa)
1437  return g_rsa;
1438 
1439  QString sName( "/RAOPKey.rsa" );
1440  FILE *file = fopen(GetConfDir().toUtf8() + sName.toUtf8(), "rb");
1441 
1442  if ( !file )
1443  {
1444  g_rsaLastError = QObject::tr("Failed to read key from: %1").arg(GetConfDir() + sName);
1445  g_rsa = NULL;
1446  LOG(VB_GENERAL, LOG_ERR, LOC + g_rsaLastError);
1447  return NULL;
1448  }
1449 
1450  g_rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL);
1451  fclose(file);
1452 
1453  if (g_rsa)
1454  {
1455  g_rsaLastError = "";
1456  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1457  QString("Loaded RSA private key (%1)").arg(RSA_check_key(g_rsa)));
1458  return g_rsa;
1459  }
1460 
1461  g_rsaLastError = QObject::tr("Failed to load RSA private key.");
1462  g_rsa = NULL;
1463  LOG(VB_GENERAL, LOG_ERR, LOC + g_rsaLastError);
1464  return NULL;
1465 }
1466 
1467 RawHash MythRAOPConnection::FindTags(const QStringList &lines)
1468 {
1469  RawHash result;
1470  if (lines.isEmpty())
1471  return result;
1472 
1473  for (int i = 0; i < lines.size(); i++)
1474  {
1475  int index = lines[i].indexOf(":");
1476  if (index > 0)
1477  {
1478  result.insert(lines[i].left(index).trimmed(),
1479  lines[i].mid(index + 1).trimmed());
1480  }
1481  }
1482  return result;
1483 }
1484 
1485 QStringList MythRAOPConnection::splitLines(const QByteArray &lines)
1486 {
1487  QStringList list;
1488  QTextStream stream(lines);
1489 
1490  QString line;
1491  do
1492  {
1493  line = stream.readLine();
1494  if (!line.isNull())
1495  {
1496  list.append(line);
1497  }
1498  }
1499  while (!line.isNull());
1500 
1501  return list;
1502 }
1503 
1512 {
1513  int hour = time / 3600;
1514  int minute = (time - hour * 3600) / 60;
1515  int seconds = time - hour * 3600 - minute * 60;
1516  QString str;
1517 
1518  if (hour)
1519  {
1520  str += QString("%1:").arg(hour);
1521  }
1522  if (minute < 10)
1523  {
1524  str += "0";
1525  }
1526  str += QString("%1:").arg(minute);
1527  if (seconds < 10)
1528  {
1529  str += "0";
1530  }
1531  str += QString::number(seconds);
1532  return str;
1533 }
1534 
1541 {
1542  return (frames * 1000ULL) / m_frameRate;
1543 }
1544 
1552 QMap<QString,QString> MythRAOPConnection::decodeDMAP(const QByteArray &dmap)
1553 {
1554  QMap<QString,QString> result;
1555  int offset = 8;
1556  while (offset < dmap.size())
1557  {
1558  QString tag = dmap.mid(offset, 4);
1559  offset += 4;
1560  uint32_t length = qFromBigEndian(*(uint32_t *)(dmap.constData() + offset));
1561  offset += sizeof(uint32_t);
1562  QString content = QString::fromUtf8(dmap.mid(offset,
1563  length).constData());
1564  offset += length;
1565  result.insert(tag, content);
1566  }
1567  return result;
1568 }
1569 
1571 {
1572  DestroyDecoder();
1573 
1574  // create an ALAC decoder
1575  avcodeclock->lock();
1576  av_register_all();
1577  avcodeclock->unlock();
1578 
1579  m_codec = avcodec_find_decoder(CODEC_ID_ALAC);
1580  if (!m_codec)
1581  {
1582  LOG(VB_GENERAL, LOG_ERR, LOC
1583  + "Failed to create ALAC decoder- going silent...");
1584  return false;
1585  }
1586 
1587  m_codeccontext = avcodec_alloc_context3(m_codec);
1588  if (m_codeccontext)
1589  {
1590  unsigned char *extradata = new unsigned char[36];
1591  memset(extradata, 0, 36);
1592  if (m_audioFormat.size() < 12)
1593  {
1594  LOG(VB_GENERAL, LOG_ERR, LOC +
1595  "Creating decoder but haven't seen audio format.");
1596  }
1597  else
1598  {
1599  uint32_t fs = m_audioFormat[1]; // frame size
1600  extradata[12] = (fs >> 24) & 0xff;
1601  extradata[13] = (fs >> 16) & 0xff;
1602  extradata[14] = (fs >> 8) & 0xff;
1603  extradata[15] = fs & 0xff;
1604  extradata[16] = m_channels; // channels
1605  extradata[17] = m_audioFormat[3]; // sample size
1606  extradata[18] = m_audioFormat[4]; // rice_historymult
1607  extradata[19] = m_audioFormat[5]; // rice_initialhistory
1608  extradata[20] = m_audioFormat[6]; // rice_kmodifier
1609  }
1610  m_codeccontext->extradata = extradata;
1611  m_codeccontext->extradata_size = 36;
1612  m_codeccontext->channels = m_channels;
1613  if (avcodec_open2(m_codeccontext, m_codec, NULL) < 0)
1614  {
1615  LOG(VB_GENERAL, LOG_ERR, LOC +
1616  "Failed to open ALAC decoder - going silent...");
1617  DestroyDecoder();
1618  return false;
1619  }
1620  LOG(VB_GENERAL, LOG_DEBUG, LOC + "Opened ALAC decoder.");
1621  }
1622 
1623  return true;
1624 }
1625 
1627 {
1628  if (m_codeccontext)
1629  {
1630  avcodec_close(m_codeccontext);
1632  }
1633  m_codec = NULL;
1634  m_codeccontext = NULL;
1635 }
1636 
1638 {
1639  CloseAudioDevice();
1640 
1641  QString passthru = gCoreContext->GetNumSetting("PassThruDeviceOverride", false)
1642  ? gCoreContext->GetSetting("PassThruOutputDevice") : QString::null;
1643  QString device = gCoreContext->GetSetting("AudioOutputDevice");
1644 
1645  m_audio = AudioOutput::OpenAudio(device, passthru, FORMAT_S16, m_channels,
1647  m_allowVolumeControl, false);
1648  if (!m_audio)
1649  {
1650  LOG(VB_GENERAL, LOG_ERR, LOC +
1651  "Failed to open audio device. Going silent...");
1652  CloseAudioDevice();
1653  StartAudioTimer();
1654  return false;
1655  }
1656 
1657  QString error = m_audio->GetError();
1658  if (!error.isEmpty())
1659  {
1660  LOG(VB_GENERAL, LOG_ERR, LOC +
1661  QString("Audio not initialised. Message was '%1'")
1662  .arg(error));
1663  CloseAudioDevice();
1664  StartAudioTimer();
1665  return false;
1666  }
1667 
1668  StopAudioTimer();
1669  LOG(VB_GENERAL, LOG_DEBUG, LOC + "Opened audio device.");
1670  return true;
1671 }
1672 
1674 {
1675  delete m_audio;
1676  m_audio = NULL;
1677 }
1678 
1680 {
1681  if (m_audioTimer)
1682  return;
1683 
1684  m_audioTimer = new QTimer();
1685  connect(m_audioTimer, SIGNAL(timeout()), this, SLOT(audioRetry()));
1686  m_audioTimer->start(5000);
1687 }
1688 
1690 {
1691  if (m_audioTimer)
1692  {
1693  m_audioTimer->stop();
1694  }
1695  delete m_audioTimer;
1696  m_audioTimer = NULL;
1697 }
1698 
1704 {
1705  if (!m_audio)
1706  return 0;
1707 
1708  int16_t *samples = (int16_t *)av_mallocz(AVCODEC_MAX_AUDIO_FRAME_SIZE);
1709  int frames = AUDIOCARD_BUFFER * m_frameRate / 1000;
1710  m_audio->AddData((char *)samples,
1711  frames * (m_sampleSize>>3) * m_channels,
1712  0,
1713  frames);
1714  av_free(samples);
1715  usleep(AUDIOCARD_BUFFER * 1000);
1716  uint64_t audiots = m_audio->GetAudiotime();
1717  LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("AudioCardLatency: ts=%1ms")
1718  .arg(audiots));
1719  return AUDIOCARD_BUFFER - (int64_t)audiots;
1720 }
1721 
1722 void MythRAOPConnection::newEventClient(QTcpSocket *client)
1723 {
1724  LOG(VB_GENERAL, LOG_INFO, LOC +
1725  QString("New connection from %1:%2 for RAOP events server.")
1726  .arg(client->peerAddress().toString()).arg(client->peerPort()));
1727 
1728  m_eventClients.append(client);
1729  connect(client, SIGNAL(disconnected()), this, SLOT(deleteEventClient()));
1730  return;
1731 }
1732 
1734 {
1735  QTcpSocket *client = static_cast<QTcpSocket *>(sender());
1736 
1737  LOG(VB_GENERAL, LOG_DEBUG, LOC +
1738  QString("%1:%2 disconnected from RAOP events server.")
1739  .arg(client->peerAddress().toString()).arg(client->peerPort()));
1740 }