mythtv  0.27.4
About: MythTV is a media-center (network streaming digital video recorder, a digital multimedia home entertainment system, or Home Theater Personal Computer).
  Fossies Dox: mythtv-0.27.4.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

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