mythtv  0.27.6
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.6.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

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 
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 
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 }
void * av_mallocz(size_t size)
Definition: mem.c:205
return t
#define RANGE_RESEND
#define NULL
Definition: dvd_udf.c:72
void SetParent(void *parent)
memset(msg.data(), 0, 100)
the library shall signal any pending events on the socket in an _edge triggered_ fashion by making the file descriptor become ready for reading or can be written the underlying socket
#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
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFilterBuffer structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining list
_NetStream(QIODevice *device)
virtual void Reset(void)=0
#define AUDIO_DATA
#define TIMING_REQUEST
void UnRegister(void *from, int id, bool closeimemdiately=false)
items[0] events
Definition: zmq_poll.txt:107
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.
trying all byte sequences megabyte in length and selecting the best looking sequence will yield cases to try But first
const char * from
Definition: jacosubdec.c:68
uint8_t * extradata
Definition: avcodec.h:1701
QString GenerateNonce(void)
#define LOG(_MASK_, _LEVEL_, _FORMAT_,...)
Definition: mythlogging.h:41
const char data[16]
Definition: mxf.c:68
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
Definition: graph2dot.c:48
void emitTVPlaybackStopped(void)
QDateTime current(bool stripped)
Definition: mythdate.cpp:10
voidpf void uLong size
Definition: ioapi.h:42
obviously you want to use this if you aren t running a mysql instance with a mythconverg database file specifies the input file outputfile specifies the output file Notably if you use as the outputfile the output is sent to the standard output very quiet specifies a compact output format and is essential when redirecting output from mythcommflag to the standard output for further processing outputmethod specifies the output format and will be explained later in this file
Definition: README.txt:13
virtual void Pause(bool paused)=0
int tryListeningPort(int baseport, int range=1)
Definition: serverpool.cpp:634
unsigned __int64 uint64_t
QString GetSetting(const QString &key, const QString &defaultval="")
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
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFilterBuffer structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample format(the sample packing is implied by the sample format) and sample rate.The lists are not just lists
bool Queue(const MythNotification &notification)
if it could not because there are no more frames
_NetStream & operator<<(const QString &str)
*DESCRIPTION The _zmq_poll() _ function provides a mechanism for applications to multiplex input/output events in a level-triggered fashion over a set of sockets.Each member of the array pointed to by the 'items'argument is a *zmq_pollitem_t *structure.The 'nitems'argument specifies the number of items in the 'items'array.The *zmq_pollitem_t *structure is defined as follows For each *zmq_pollitem_t * item
Definition: zmq_poll.txt:18
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
int len
uint8_t * data[AVRESAMPLE_MAX_CHANNELS]
samples buffer
Definition: audio_data.h:37
virtual bool IsPaused(void) const =0
AVCodec * avcodec_find_decoder(enum AVCodecID id)
Definition: utils.c:2138
PictureAttribute next(PictureAttributeSupported supported, PictureAttribute attribute)
memcpy(data,"ABCDEF", 6)
MythRAOPConnection(QObject *parent, QTcpSocket *socket, QByteArray id, int port)
int GetNumSetting(const QString &key, int defaultval=0)
var n
int extradata_size
Definition: avcodec.h:1702
voidpf stream
Definition: ioapi.h:42
var q
Definition: jquery.min.js:16
virtual bool AddData(void *buffer, int len, int64_t timecode, int frames)=0
int index
Definition: gxfenc.c:89
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.
qint64 writeDatagram(const char *data, qint64 size, const QHostAddress &addr, quint16 port)
Definition: serverpool.cpp:553
signed __int64 int64_t
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
Definition: utils.c:823
signed __int16 int16_t
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:667
#define FIRSTAUDIO_DATA
#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
function type
Definition: jquery.min.js:16
c fn position
#define RAOP_PORT_RANGE
static Type Update
void WantingPlayback(QObject *sender)
Filter the word “frame” indicates either a video frame or a group of audio samples
return f
unsigned __int16 uint16_t
var i
Definition: jquery.min.js:16
s connect("tcp://192.168.0.115:5555")
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