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