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