|
MythTV
0.26-pre
|
00001 #include <QTimer> 00002 #include <QTcpSocket> 00003 #include <QtEndian> 00004 #include <QTextStream> 00005 00006 #include "mythlogging.h" 00007 #include "mythcorecontext.h" 00008 #include "mythdirs.h" 00009 #include "serverpool.h" 00010 00011 #include "audiooutput.h" 00012 00013 #include "mythraopdevice.h" 00014 #include "mythraopconnection.h" 00015 00016 #define LOC QString("RAOP Conn: ") 00017 #define MAX_PACKET_SIZE 2048 00018 00019 RSA* MythRAOPConnection::g_rsa = NULL; 00020 00021 // RAOP RTP packet type 00022 #define TIMING_REQUEST 0x52 00023 #define TIMING_RESPONSE 0x53 00024 #define SYNC 0x54 00025 #define FIRSTSYNC (0x54 | 0x80) 00026 #define RANGE_RESEND 0x55 00027 #define AUDIO_RESEND 0x56 00028 #define AUDIO_DATA 0x60 00029 #define FIRSTAUDIO_DATA (0x60 | 0x80) 00030 00031 00032 // Size (in ms) of audio buffered in audio card 00033 #define AUDIOCARD_BUFFER 800 00034 // How frequently we may call ProcessAudio (via QTimer) 00035 // ideally 20ms, but according to documentation 00036 // anything lower than 50ms on windows, isn't reliable 00037 #define AUDIO_BUFFER 100 00038 00039 class NetStream : public QTextStream 00040 { 00041 public: 00042 NetStream(QIODevice *device) : QTextStream(device) 00043 { 00044 }; 00045 NetStream &operator<<(const QString &str) 00046 { 00047 LOG(VB_GENERAL, LOG_DEBUG, 00048 LOC + QString("Sending(%1): ").arg(str.length()) + str); 00049 QTextStream *q = this; 00050 *q << str; 00051 return *this; 00052 }; 00053 }; 00054 00055 MythRAOPConnection::MythRAOPConnection(QObject *parent, QTcpSocket *socket, 00056 QByteArray id, int port) 00057 : QObject(parent), m_watchdogTimer(NULL), m_socket(socket), 00058 m_textStream(NULL), m_hardwareId(id), 00059 m_incomingHeaders(), m_incomingContent(), m_incomingPartial(false), 00060 m_incomingSize(0), 00061 m_dataSocket(NULL), m_dataPort(port), 00062 m_clientControlSocket(NULL), m_clientControlPort(0), 00063 m_clientTimingSocket(NULL), m_clientTimingPort(0), 00064 m_audio(NULL), m_codec(NULL), m_codeccontext(NULL), 00065 m_channels(2), m_sampleSize(16), m_frameRate(44100), 00066 m_framesPerPacket(352),m_dequeueAudioTimer(NULL), 00067 m_queueLength(0), m_streamingStarted(false), 00068 m_allowVolumeControl(true), 00069 //audio sync 00070 m_seqNum(0), 00071 m_lastSequence(0), m_lastTimestamp(0), 00072 m_currentTimestamp(0), m_nextSequence(0), m_nextTimestamp(0), 00073 m_bufferLength(0), m_timeLastSync(0), 00074 m_cardLatency(0), m_adjustedLatency(0), m_audioStarted(false), 00075 // clock sync 00076 m_masterTimeStamp(0), m_deviceTimeStamp(0), m_networkLatency(0), 00077 m_clockSkew(0), 00078 m_audioTimer(NULL), 00079 m_progressStart(0), m_progressCurrent(0), m_progressEnd(0) 00080 { 00081 } 00082 00083 MythRAOPConnection::~MythRAOPConnection() 00084 { 00085 // delete audio timer 00086 StopAudioTimer(); 00087 00088 // stop and delete watchdog timer 00089 if (m_watchdogTimer) 00090 { 00091 m_watchdogTimer->stop(); 00092 delete m_watchdogTimer; 00093 } 00094 00095 if (m_dequeueAudioTimer) 00096 { 00097 m_dequeueAudioTimer->stop(); 00098 delete m_dequeueAudioTimer; 00099 } 00100 00101 // delete main socket 00102 if (m_socket) 00103 { 00104 m_socket->close(); 00105 m_socket->deleteLater(); 00106 } 00107 00108 // delete data socket 00109 if (m_dataSocket) 00110 { 00111 m_dataSocket->disconnect(); 00112 m_dataSocket->close(); 00113 m_dataSocket->deleteLater(); 00114 } 00115 00116 // client control socket 00117 if (m_clientControlSocket) 00118 { 00119 m_clientControlSocket->disconnect(); 00120 m_clientControlSocket->close(); 00121 m_clientControlSocket->deleteLater(); 00122 } 00123 00124 // close audio decoder 00125 DestroyDecoder(); 00126 00127 // free decoded audio buffer 00128 ResetAudio(); 00129 00130 // close audio device 00131 CloseAudioDevice(); 00132 } 00133 00134 bool MythRAOPConnection::Init(void) 00135 { 00136 // connect up the request socket 00137 m_textStream = new NetStream(m_socket); 00138 m_textStream->setCodec("UTF-8"); 00139 if (!connect(m_socket, SIGNAL(readyRead()), this, SLOT(readClient()))) 00140 { 00141 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect client socket signal."); 00142 return false; 00143 } 00144 00145 // create the data socket 00146 m_dataSocket = new ServerPool(); 00147 if (!connect(m_dataSocket, SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)), 00148 this, SLOT(udpDataReady(QByteArray, QHostAddress, quint16)))) 00149 { 00150 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect data socket signal."); 00151 return false; 00152 } 00153 00154 // try a few ports in case the first is in use 00155 m_dataPort = m_dataSocket->tryBindingPort(m_dataPort, RAOP_PORT_RANGE); 00156 if (m_dataPort < 0) 00157 { 00158 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to bind to a port for data."); 00159 return false; 00160 } 00161 00162 LOG(VB_GENERAL, LOG_INFO, LOC + 00163 QString("Bound to port %1 for incoming data").arg(m_dataPort)); 00164 00165 // load the private key 00166 if (!LoadKey()) 00167 return false; 00168 00169 // use internal volume control 00170 m_allowVolumeControl = gCoreContext->GetNumSetting("MythControlsVolume", 1); 00171 00172 // start the watchdog timer to auto delete the client after a period of inactivity 00173 m_watchdogTimer = new QTimer(); 00174 connect(m_watchdogTimer, SIGNAL(timeout()), this, SLOT(timeout())); 00175 m_watchdogTimer->start(10000); 00176 00177 m_dequeueAudioTimer = new QTimer(); 00178 connect(m_dequeueAudioTimer, SIGNAL(timeout()), this, SLOT(ProcessAudio())); 00179 00180 return true; 00181 } 00182 00187 void MythRAOPConnection::udpDataReady(QByteArray buf, QHostAddress peer, 00188 quint16 port) 00189 { 00190 // restart the idle timer 00191 if (m_watchdogTimer) 00192 m_watchdogTimer->start(10000); 00193 00194 if (!m_audio || !m_codec || !m_codeccontext) 00195 return; 00196 00197 uint8_t type; 00198 uint16_t seq; 00199 uint64_t timestamp; 00200 00201 if (!GetPacketType(buf, type, seq, timestamp)) 00202 { 00203 LOG(VB_GENERAL, LOG_DEBUG, LOC + 00204 QString("Packet doesn't start with valid Rtp Header (0x%1)") 00205 .arg((uint8_t)buf[0], 0, 16)); 00206 return; 00207 } 00208 00209 switch (type) 00210 { 00211 case SYNC: 00212 case FIRSTSYNC: 00213 ProcessSync(buf); 00214 ProcessAudio(); 00215 return; 00216 00217 case FIRSTAUDIO_DATA: 00218 m_nextSequence = seq; 00219 m_nextTimestamp = timestamp; 00220 // With iTunes we know what the first sequence is going to be. 00221 // iOS device do not tell us before streaming start what the first 00222 // packet is going to be. 00223 m_streamingStarted = true; 00224 break; 00225 00226 case AUDIO_DATA: 00227 case AUDIO_RESEND: 00228 break; 00229 00230 case TIMING_RESPONSE: 00231 ProcessTimeResponse(buf); 00232 return; 00233 00234 default: 00235 LOG(VB_GENERAL, LOG_DEBUG, LOC + 00236 QString("Packet type (0x%1) not handled") 00237 .arg(type, 0, 16)); 00238 return; 00239 } 00240 00241 timestamp = framesToMs(timestamp); 00242 if (timestamp < m_currentTimestamp) 00243 { 00244 LOG(VB_GENERAL, LOG_DEBUG, LOC + 00245 QString("Received packet %1 too late, ignoring") 00246 .arg(seq)); 00247 return; 00248 } 00249 // regular data packet 00250 if (type == AUDIO_DATA || type == FIRSTAUDIO_DATA) 00251 { 00252 if (m_streamingStarted && seq != m_nextSequence) 00253 SendResendRequest(timestamp, m_nextSequence, seq); 00254 00255 m_nextSequence = seq + 1; 00256 m_nextTimestamp = timestamp; 00257 m_streamingStarted = true; 00258 } 00259 00260 if (!m_streamingStarted) 00261 return; 00262 00263 // resent packet 00264 if (type == AUDIO_RESEND) 00265 { 00266 if (m_resends.contains(seq)) 00267 { 00268 LOG(VB_GENERAL, LOG_DEBUG, LOC + 00269 QString("Received required resend %1 (with ts:%2 last:%3)") 00270 .arg(seq).arg(timestamp).arg(m_nextSequence)); 00271 m_resends.remove(seq); 00272 } 00273 else 00274 LOG(VB_GENERAL, LOG_WARNING, LOC + 00275 QString("Received unexpected resent packet %1") 00276 .arg(seq)); 00277 } 00278 00279 // Check that the audio packet is valid, do so by decoding it. If an error 00280 // occurs, ask to resend it 00281 QList<AudioData> *decoded = new QList<AudioData>(); 00282 int numframes = decodeAudioPacket(type, &buf, decoded); 00283 if (numframes < 0) 00284 { 00285 // an error occurred, ask for the audio packet once again. 00286 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Error decoding audio")); 00287 SendResendRequest(timestamp, seq, seq+1); 00288 return; 00289 } 00290 AudioPacket frames; 00291 frames.seq = seq; 00292 frames.data = decoded; 00293 m_audioQueue.insert(timestamp, frames); 00294 ProcessAudio(); 00295 } 00296 00297 void MythRAOPConnection::ProcessSync(const QByteArray &buf) 00298 { 00299 bool first = (uint8_t)buf[0] == 0x90; // First sync is 0x90,0x55 00300 const char *req = buf.constData(); 00301 uint64_t current_ts = qFromBigEndian(*(uint32_t*)(req + 4)); 00302 uint64_t next_ts = qFromBigEndian(*(uint32_t*)(req + 16)); 00303 00304 uint64_t current = framesToMs(current_ts); 00305 uint64_t next = framesToMs(next_ts); 00306 00307 m_currentTimestamp = current; 00308 m_nextTimestamp = next; 00309 m_bufferLength = m_nextTimestamp - m_currentTimestamp; 00310 00311 if (first) 00312 { 00313 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Receiving first SYNC packet")); 00314 } 00315 else 00316 { 00317 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Receiving SYNC packet")); 00318 } 00319 00320 timeval t; gettimeofday(&t, NULL); 00321 m_timeLastSync = t.tv_sec * 1000 + t.tv_usec / 1000; 00322 00323 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("SYNC: cur:%1 next:%2 time:%3") 00324 .arg(m_currentTimestamp).arg(m_nextTimestamp).arg(m_timeLastSync)); 00325 00326 uint64_t delay = framesToMs(m_audioQueue.size() * m_framesPerPacket); 00327 delay += m_networkLatency; 00328 00329 // Calculate audio card latency 00330 if (first) 00331 { 00332 m_cardLatency = AudioCardLatency(); 00333 // if audio isn't started, start playing 200ms worth of silence 00334 // and measure timestamp difference 00335 LOG(VB_GENERAL, LOG_DEBUG, LOC + 00336 QString("Audio hardware latency: %1ms").arg(m_cardLatency)); 00337 } 00338 00339 uint64_t audiots = m_audio->GetAudiotime(); 00340 if (m_audioStarted) 00341 { 00342 m_adjustedLatency = (int64_t)audiots - (int64_t)m_currentTimestamp; 00343 } 00344 if (m_adjustedLatency > (int64_t)m_bufferLength) 00345 { 00346 // Too much delay in playback 00347 // will reset audio card in next ProcessAudio 00348 m_audioStarted = false; 00349 m_adjustedLatency = 0; 00350 } 00351 00352 delay += m_audio->GetAudioBufferedTime(); 00353 delay += m_adjustedLatency; 00354 00355 // Expire old audio 00356 ExpireResendRequests(m_currentTimestamp); 00357 int res = ExpireAudio(m_currentTimestamp); 00358 if (res > 0) 00359 { 00360 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Drop %1 packets").arg(res)); 00361 } 00362 00363 LOG(VB_GENERAL, LOG_DEBUG, LOC + 00364 QString("Queue=%1 buffer=%2ms ideal=%3ms diffts:%4ms") 00365 .arg(m_audioQueue.size()) 00366 .arg(delay) 00367 .arg(m_bufferLength) 00368 .arg(m_adjustedLatency)); 00369 } 00370 00375 void MythRAOPConnection::SendResendRequest(uint64_t timestamp, 00376 uint16_t expected, uint16_t got) 00377 { 00378 if (!m_clientControlSocket) 00379 return; 00380 00381 int16_t missed = (got < expected) ? 00382 (int16_t)(((int32_t)got + UINT16_MAX + 1) - expected) : 00383 got - expected; 00384 00385 LOG(VB_GENERAL, LOG_INFO, LOC + 00386 QString("Missed %1 packet(s): expected %2 got %3 ts:%4") 00387 .arg(missed).arg(expected).arg(got).arg(timestamp)); 00388 00389 char req[8]; 00390 req[0] = 0x80; 00391 req[1] = RANGE_RESEND | 0x80; 00392 *(uint16_t *)(req + 2) = qToBigEndian(m_seqNum++); 00393 *(uint16_t *)(req + 4) = qToBigEndian(expected); // missed seqnum 00394 *(uint16_t *)(req + 6) = qToBigEndian(missed); // count 00395 00396 if (m_clientControlSocket->writeDatagram(req, sizeof(req), 00397 m_peerAddress, m_clientControlPort) 00398 == sizeof(req)) 00399 { 00400 for (uint16_t count = 0; count < missed; count++) 00401 { 00402 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Sent resend for %1") 00403 .arg(expected + count)); 00404 m_resends.insert(expected + count, timestamp); 00405 } 00406 } 00407 else 00408 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to send resend request."); 00409 } 00410 00416 void MythRAOPConnection::ExpireResendRequests(uint64_t timestamp) 00417 { 00418 if (!m_resends.size()) 00419 return; 00420 00421 QMutableMapIterator<uint16_t,uint64_t> it(m_resends); 00422 while (it.hasNext()) 00423 { 00424 it.next(); 00425 if (it.value() < timestamp && m_streamingStarted) 00426 { 00427 LOG(VB_GENERAL, LOG_WARNING, LOC + 00428 QString("Never received resend packet %1").arg(it.key())); 00429 m_resends.remove(it.key()); 00430 } 00431 } 00432 } 00433 00438 void MythRAOPConnection::SendTimeRequest(void) 00439 { 00440 if (!m_clientControlSocket) // should never happen 00441 return; 00442 00443 timeval t; 00444 gettimeofday(&t, NULL); 00445 00446 char req[32]; 00447 req[0] = 0x80; 00448 req[1] = TIMING_REQUEST | 0x80; 00449 // this is always 0x00 0x07 according to http://blog.technologeek.org/airtunes-v2 00450 // no other value works 00451 req[2] = 0x00; 00452 req[3] = 0x07; 00453 *(uint32_t *)(req + 4) = (uint32_t)0; 00454 *(uint64_t *)(req + 8) = (uint64_t)0; 00455 *(uint64_t *)(req + 16) = (uint64_t)0; 00456 *(uint32_t *)(req + 24) = qToBigEndian((uint32_t)t.tv_sec); 00457 *(uint32_t *)(req + 28) = qToBigEndian((uint32_t)t.tv_usec); 00458 00459 if (m_clientTimingSocket->writeDatagram(req, sizeof(req), m_peerAddress, m_clientTimingPort) != sizeof(req)) 00460 { 00461 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to send resend time request."); 00462 return; 00463 } 00464 LOG(VB_GENERAL, LOG_DEBUG, LOC + 00465 QString("Requesting master time (Local %1.%2)") 00466 .arg(t.tv_sec).arg(t.tv_usec)); 00467 } 00468 00475 void MythRAOPConnection::ProcessTimeResponse(const QByteArray &buf) 00476 { 00477 timeval t1, t2; 00478 const char *req = buf.constData(); 00479 00480 t1.tv_sec = qFromBigEndian(*(uint32_t*)(req + 8)); 00481 t1.tv_usec = qFromBigEndian(*(uint32_t*)(req + 12)); 00482 00483 gettimeofday(&t2, NULL); 00484 uint64_t time1, time2; 00485 time1 = t1.tv_sec * 1000 + t1.tv_usec / 1000; 00486 time2 = t2.tv_sec * 1000 + t2.tv_usec / 1000; 00487 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Read back time (Local %1.%2)") 00488 .arg(t1.tv_sec).arg(t1.tv_usec)); 00489 // network latency equal time difference in ms between request and response 00490 // divide by two for approximate time of one way trip 00491 m_networkLatency = (time2 - time1) / 2; 00492 00493 // now calculate the time difference between the client and us. 00494 // this is NTP time, where sec is in seconds, and ticks is in 1/2^32s 00495 uint32_t sec = qFromBigEndian(*(uint32_t*)(req + 24)); 00496 uint32_t ticks = qFromBigEndian(*(uint32_t*)(req + 28)); 00497 // convert ticks into ms 00498 int64_t master = NTPToLocal(sec, ticks); 00499 m_clockSkew = master - time2; 00500 } 00501 00502 uint64_t MythRAOPConnection::NTPToLocal(uint32_t sec, uint32_t ticks) 00503 { 00504 return (int64_t)sec * 1000LL + (((int64_t)ticks * 1000LL) >> 32); 00505 } 00506 00507 bool MythRAOPConnection::GetPacketType(const QByteArray &buf, uint8_t &type, 00508 uint16_t &seq, uint64_t ×tamp) 00509 { 00510 // All RAOP packets start with | 0x80/0x90 (first sync) | PACKET_TYPE | 00511 if ((uint8_t)buf[0] != 0x80 && (uint8_t)buf[0] != 0x90) 00512 { 00513 return false; 00514 } 00515 00516 type = (char)buf[1]; 00517 // Is it first sync packet? 00518 if ((uint8_t)buf[0] == 0x90 && type == FIRSTSYNC) 00519 { 00520 return true; 00521 } 00522 if (type != FIRSTAUDIO_DATA) 00523 { 00524 type &= ~0x80; 00525 } 00526 00527 if (type != AUDIO_DATA && type != FIRSTAUDIO_DATA && type != AUDIO_RESEND) 00528 return true; 00529 00530 const char *ptr = buf.constData(); 00531 if (type == AUDIO_RESEND) 00532 { 00533 ptr += 4; 00534 } 00535 seq = qFromBigEndian(*(uint16_t *)(ptr + 2)); 00536 timestamp = qFromBigEndian(*(uint32_t*)(ptr + 4)); 00537 return true; 00538 } 00539 00540 // Audio decode / playback related routines 00541 00542 uint32_t MythRAOPConnection::decodeAudioPacket(uint8_t type, 00543 const QByteArray *buf, 00544 QList<AudioData> *dest) 00545 { 00546 const char *data_in = buf->constData(); 00547 int len = buf->size(); 00548 if (type == AUDIO_RESEND) 00549 { 00550 data_in += 4; 00551 len -= 4; 00552 } 00553 data_in += 12; 00554 len -= 12; 00555 if (len < 16) 00556 return -1; 00557 00558 int aeslen = len & ~0xf; 00559 unsigned char iv[16]; 00560 unsigned char decrypted_data[MAX_PACKET_SIZE]; 00561 memcpy(iv, m_AESIV.constData(), sizeof(iv)); 00562 AES_cbc_encrypt((const unsigned char*)data_in, 00563 decrypted_data, aeslen, 00564 &m_aesKey, iv, AES_DECRYPT); 00565 memcpy(decrypted_data + aeslen, data_in + aeslen, len - aeslen); 00566 00567 AVPacket tmp_pkt; 00568 AVCodecContext *ctx = m_codeccontext; 00569 00570 av_init_packet(&tmp_pkt); 00571 tmp_pkt.data = decrypted_data; 00572 tmp_pkt.size = len; 00573 00574 uint32_t frames_added = 0; 00575 while (tmp_pkt.size > 0) 00576 { 00577 uint8_t *samples = (uint8_t *)av_mallocz(AVCODEC_MAX_AUDIO_FRAME_SIZE); 00578 AVFrame frame; 00579 int got_frame = 0; 00580 00581 int ret = avcodec_decode_audio4(ctx, &frame, &got_frame, &tmp_pkt); 00582 00583 if (ret < 0) 00584 { 00585 av_free(samples); 00586 return -1; 00587 } 00588 00589 if (got_frame) 00590 { 00591 // ALAC codec isn't planar 00592 int data_size = av_samples_get_buffer_size(NULL, ctx->channels, 00593 frame.nb_samples, 00594 ctx->sample_fmt, 1); 00595 memcpy(samples, frame.extended_data[0], data_size); 00596 00597 frames_added += frame.nb_samples; 00598 AudioData block; 00599 block.data = samples; 00600 block.length = data_size; 00601 block.frames = frame.nb_samples; 00602 dest->append(block); 00603 } 00604 tmp_pkt.data += ret; 00605 tmp_pkt.size -= ret; 00606 } 00607 return frames_added; 00608 } 00609 00610 void MythRAOPConnection::ProcessAudio() 00611 { 00612 if (!m_streamingStarted || !m_audio) 00613 return; 00614 00615 if (m_audio->IsPaused()) 00616 { 00617 // ALSA takes a while to unpause, enough to have SYNC starting to drop 00618 // packets, so unpause as early as possible 00619 m_audio->Pause(false); 00620 } 00621 timeval t; gettimeofday(&t, NULL); 00622 uint64_t dtime = (t.tv_sec * 1000 + t.tv_usec / 1000) - m_timeLastSync; 00623 uint64_t rtp = dtime + m_currentTimestamp + m_networkLatency; 00624 uint64_t buffered = m_audioStarted ? m_audio->GetAudioBufferedTime() : 0; 00625 00626 // Keep audio framework buffer as short as possible, keeping everything in 00627 // m_audioQueue, so we can easily reset the least amount possible 00628 if (buffered > AUDIOCARD_BUFFER) 00629 return; 00630 00631 // Also make sure m_audioQueue never goes to less than 1/3 of the RDP stream 00632 // total latency, this should gives us enough time to receive missed packets 00633 uint64_t queue = framesToMs(m_audioQueue.size() * m_framesPerPacket); 00634 if (queue < m_bufferLength / 3) 00635 return; 00636 00637 rtp += buffered; 00638 rtp += m_cardLatency; 00639 00640 // How many packets to add to the audio card, to fill AUDIOCARD_BUFFER 00641 int max_packets = ((AUDIOCARD_BUFFER - buffered) 00642 * m_frameRate / 1000) / m_framesPerPacket; 00643 int i = 0; 00644 uint64_t timestamp = 0; 00645 00646 QMapIterator<uint64_t,AudioPacket> packet_it(m_audioQueue); 00647 while (packet_it.hasNext() && i <= max_packets) 00648 { 00649 packet_it.next(); 00650 00651 timestamp = packet_it.key(); 00652 if (timestamp < rtp) 00653 { 00654 if (!m_audioStarted) 00655 { 00656 m_audio->Reset(); // clear audio card 00657 } 00658 AudioPacket frames = packet_it.value(); 00659 00660 if (m_lastSequence != frames.seq) 00661 { 00662 LOG(VB_GENERAL, LOG_ERR, LOC + 00663 QString("Audio discontinuity seen. Played %1 (%3) expected %2") 00664 .arg(frames.seq).arg(m_lastSequence).arg(timestamp)); 00665 m_lastSequence = frames.seq; 00666 } 00667 m_lastSequence++; 00668 00669 QList<AudioData>::iterator it = frames.data->begin(); 00670 for (; it != frames.data->end(); ++it) 00671 { 00672 AudioData *data = &(*it); 00673 m_audio->AddData((char *)data->data, data->length, 00674 timestamp, data->frames); 00675 timestamp += m_audio->LengthLastData(); 00676 } 00677 i++; 00678 m_audioStarted = true; 00679 } 00680 else // QMap is sorted, so no need to continue if not found 00681 break; 00682 } 00683 00684 ExpireAudio(timestamp); 00685 m_lastTimestamp = timestamp; 00686 00687 // restart audio timer should we stop receiving data on regular interval, 00688 // we need to continue processing the audio queue 00689 m_dequeueAudioTimer->start(AUDIO_BUFFER); 00690 } 00691 00692 int MythRAOPConnection::ExpireAudio(uint64_t timestamp) 00693 { 00694 int res = 0; 00695 QMutableMapIterator<uint64_t,AudioPacket> packet_it(m_audioQueue); 00696 while (packet_it.hasNext()) 00697 { 00698 packet_it.next(); 00699 if (packet_it.key() < timestamp) 00700 { 00701 AudioPacket frames = packet_it.value(); 00702 if (frames.data) 00703 { 00704 QList<AudioData>::iterator it = frames.data->begin(); 00705 for (; it != frames.data->end(); ++it) 00706 { 00707 av_free(it->data); 00708 } 00709 delete frames.data; 00710 } 00711 m_audioQueue.remove(packet_it.key()); 00712 res++; 00713 } 00714 } 00715 return res; 00716 } 00717 00718 void MythRAOPConnection::ResetAudio(void) 00719 { 00720 if (m_audio) 00721 { 00722 m_audio->Reset(); 00723 } 00724 ExpireAudio(UINT64_MAX); 00725 ExpireResendRequests(UINT64_MAX); 00726 m_audioStarted = false; 00727 } 00728 00729 void MythRAOPConnection::timeout(void) 00730 { 00731 LOG(VB_GENERAL, LOG_INFO, LOC + "Closing connection after inactivity."); 00732 m_socket->disconnectFromHost(); 00733 } 00734 00735 void MythRAOPConnection::audioRetry(void) 00736 { 00737 if (!m_audio) 00738 { 00739 MythRAOPDevice* p = (MythRAOPDevice*)parent(); 00740 if (p && p->NextInAudioQueue(this) && OpenAudioDevice()) 00741 { 00742 CreateDecoder(); 00743 } 00744 } 00745 00746 if (m_audio && m_codec && m_codeccontext) 00747 { 00748 StopAudioTimer(); 00749 } 00750 } 00751 00756 void MythRAOPConnection::readClient(void) 00757 { 00758 QTcpSocket *socket = (QTcpSocket *)sender(); 00759 if (!socket) 00760 return; 00761 00762 QByteArray data = socket->readAll(); 00763 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("readClient(%1): ") 00764 .arg(data.size()) + data.constData()); 00765 00766 // For big content, we may be called several times for a single packet 00767 if (!m_incomingPartial) 00768 { 00769 m_incomingHeaders.clear(); 00770 m_incomingContent.clear(); 00771 m_incomingSize = 0; 00772 00773 QTextStream stream(data); 00774 QString line; 00775 do 00776 { 00777 line = stream.readLine(); 00778 if (line.size() == 0) 00779 break; 00780 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Header = %1").arg(line)); 00781 m_incomingHeaders.append(line); 00782 if (line.contains("Content-Length:")) 00783 { 00784 m_incomingSize = line.mid(line.indexOf(" ") + 1).toInt(); 00785 } 00786 } 00787 while (!line.isNull()); 00788 00789 if (m_incomingHeaders.size() == 0) 00790 return; 00791 00792 if (!stream.atEnd()) 00793 { 00794 int pos = stream.pos(); 00795 if (pos > 0) 00796 { 00797 m_incomingContent.append(data.mid(pos)); 00798 } 00799 } 00800 } 00801 else 00802 { 00803 m_incomingContent.append(data); 00804 } 00805 00806 // If we haven't received all the content yet, wait (see when receiving 00807 // coverart 00808 if (m_incomingContent.size() < m_incomingSize) 00809 { 00810 m_incomingPartial = true; 00811 return; 00812 } 00813 else 00814 { 00815 m_incomingPartial = false; 00816 } 00817 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Content(%1) = %2") 00818 .arg(m_incomingContent.size()).arg(m_incomingContent.constData())); 00819 00820 ProcessRequest(m_incomingHeaders, m_incomingContent); 00821 } 00822 00823 void MythRAOPConnection::ProcessRequest(const QStringList &header, 00824 const QByteArray &content) 00825 { 00826 if (header.isEmpty()) 00827 return; 00828 00829 RawHash tags = FindTags(header); 00830 00831 if (!tags.contains("CSeq")) 00832 { 00833 LOG(VB_GENERAL, LOG_ERR, LOC + "ProcessRequest: Didn't find CSeq"); 00834 return; 00835 } 00836 00837 *m_textStream << "RTSP/1.0 200 OK\r\n"; 00838 00839 QString option = header[0].left(header[0].indexOf(" ")); 00840 00841 // process RTP-info field 00842 bool gotRTP = false; 00843 uint16_t RTPseq; 00844 uint64_t RTPtimestamp; 00845 if (tags.contains("RTP-Info")) 00846 { 00847 gotRTP = true; 00848 QString data = tags["RTP-Info"]; 00849 QStringList items = data.split(";"); 00850 foreach (QString item, items) 00851 { 00852 if (item.startsWith("seq")) 00853 { 00854 RTPseq = item.mid(item.indexOf("=") + 1).trimmed().toUShort(); 00855 } 00856 else if (item.startsWith("rtptime")) 00857 { 00858 RTPtimestamp = item.mid(item.indexOf("=") + 1).trimmed().toUInt(); 00859 } 00860 } 00861 LOG(VB_GENERAL, LOG_INFO, LOC + QString("RTP-Info: seq=%1 rtptime=%2") 00862 .arg(RTPseq).arg(RTPtimestamp)); 00863 } 00864 00865 if (option == "OPTIONS") 00866 { 00867 if (tags.contains("Apple-Challenge")) 00868 { 00869 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Received Apple-Challenge")); 00870 00871 *m_textStream << "Apple-Response: "; 00872 if (!LoadKey()) 00873 return; 00874 int tosize = RSA_size(LoadKey()); 00875 uint8_t *to = new uint8_t[tosize]; 00876 00877 QByteArray challenge = 00878 QByteArray::fromBase64(tags["Apple-Challenge"].toAscii()); 00879 int challenge_size = challenge.size(); 00880 if (challenge_size != 16) 00881 { 00882 LOG(VB_GENERAL, LOG_ERR, LOC + 00883 QString("Decoded challenge size %1, expected 16") 00884 .arg(challenge_size)); 00885 if (challenge_size > 16) 00886 challenge_size = 16; 00887 } 00888 00889 int i = 0; 00890 unsigned char from[38]; 00891 memcpy(from, challenge.constData(), challenge_size); 00892 i += challenge_size; 00893 if (m_socket->localAddress().protocol() == 00894 QAbstractSocket::IPv4Protocol) 00895 { 00896 uint32_t ip = m_socket->localAddress().toIPv4Address(); 00897 ip = qToBigEndian(ip); 00898 memcpy(from + i, &ip, 4); 00899 i += 4; 00900 } 00901 else if (m_socket->localAddress().protocol() == 00902 QAbstractSocket::IPv6Protocol) 00903 { 00904 Q_IPV6ADDR ip = m_socket->localAddress().toIPv6Address(); 00905 if(memcmp(&ip, 00906 "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\xff\xff", 00907 12) == 0) 00908 { 00909 memcpy(from + i, &ip[12], 4); 00910 i += 4; 00911 } 00912 else 00913 { 00914 memcpy(from + i, &ip, 16); 00915 i += 16; 00916 } 00917 } 00918 memcpy(from + i, m_hardwareId.constData(), RAOP_HARDWARE_ID_SIZE); 00919 i += RAOP_HARDWARE_ID_SIZE; 00920 00921 int pad = 32 - i; 00922 if (pad > 0) 00923 { 00924 memset(from + i, 0, pad); 00925 i += pad; 00926 } 00927 00928 LOG(VB_GENERAL, LOG_DEBUG, LOC + 00929 QString("Full base64 response: '%1' size %2") 00930 .arg(QByteArray((const char*)from, i).toBase64().constData()) 00931 .arg(i)); 00932 00933 RSA_private_encrypt(i, from, to, LoadKey(), RSA_PKCS1_PADDING); 00934 00935 QByteArray base64 = QByteArray((const char*)to, tosize).toBase64(); 00936 delete[] to; 00937 00938 for (int pos = base64.size() - 1; pos > 0; pos--) 00939 { 00940 if (base64[pos] == '=') 00941 base64[pos] = ' '; 00942 else 00943 break; 00944 } 00945 LOG(VB_GENERAL, LOG_DEBUG, QString("tSize=%1 tLen=%2 tResponse=%3") 00946 .arg(tosize).arg(base64.size()).arg(base64.constData())); 00947 *m_textStream << base64.trimmed() << "\r\n"; 00948 } 00949 StartResponse(m_textStream, option, tags["CSeq"]); 00950 *m_textStream << "Public: ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, " 00951 "TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER, POST, GET\r\n"; 00952 } 00953 else if (option == "ANNOUNCE") 00954 { 00955 QStringList lines = splitLines(content); 00956 foreach (QString line, lines) 00957 { 00958 if (line.startsWith("a=rsaaeskey:")) 00959 { 00960 QString key = line.mid(12).trimmed(); 00961 QByteArray decodedkey = QByteArray::fromBase64(key.toAscii()); 00962 LOG(VB_GENERAL, LOG_DEBUG, LOC + 00963 QString("RSAAESKey: %1 (decoded size %2)") 00964 .arg(key).arg(decodedkey.size())); 00965 00966 if (LoadKey()) 00967 { 00968 int size = sizeof(char) * RSA_size(LoadKey()); 00969 char *decryptedkey = new char[size]; 00970 if (RSA_private_decrypt(decodedkey.size(), 00971 (const unsigned char*)decodedkey.constData(), 00972 (unsigned char*)decryptedkey, 00973 LoadKey(), RSA_PKCS1_OAEP_PADDING)) 00974 { 00975 LOG(VB_GENERAL, LOG_DEBUG, LOC + 00976 "Successfully decrypted AES key from RSA."); 00977 AES_set_decrypt_key((const unsigned char*)decryptedkey, 00978 128, &m_aesKey); 00979 } 00980 else 00981 { 00982 LOG(VB_GENERAL, LOG_WARNING, LOC + 00983 "Failed to decrypt AES key from RSA."); 00984 } 00985 delete [] decryptedkey; 00986 } 00987 00988 } 00989 else if (line.startsWith("a=aesiv:")) 00990 { 00991 QString aesiv = line.mid(8).trimmed(); 00992 m_AESIV = QByteArray::fromBase64(aesiv.toAscii()); 00993 LOG(VB_GENERAL, LOG_DEBUG, LOC + 00994 QString("AESIV: %1 (decoded size %2)") 00995 .arg(aesiv).arg(m_AESIV.size())); 00996 } 00997 else if (line.startsWith("a=fmtp:")) 00998 { 00999 m_audioFormat.clear(); 01000 QString format = line.mid(7).trimmed(); 01001 QList<QString> fmts = format.split(' '); 01002 foreach (QString fmt, fmts) 01003 m_audioFormat.append(fmt.toInt()); 01004 01005 foreach (int fmt, m_audioFormat) 01006 LOG(VB_GENERAL, LOG_DEBUG, LOC + 01007 QString("Audio parameter: %1").arg(fmt)); 01008 m_framesPerPacket = m_audioFormat[1]; 01009 m_sampleSize = m_audioFormat[3]; 01010 m_channels = m_audioFormat[7]; 01011 m_frameRate = m_audioFormat[11]; 01012 } 01013 } 01014 StartResponse(m_textStream, option, tags["CSeq"]); 01015 } 01016 else if (option == "SETUP") 01017 { 01018 if (tags.contains("Transport")) 01019 { 01020 int control_port = 0; 01021 int timing_port = 0; 01022 QString data = tags["Transport"]; 01023 QStringList items = data.split(";"); 01024 01025 foreach (QString item, items) 01026 { 01027 if (item.startsWith("control_port")) 01028 control_port = item.mid(item.indexOf("=") + 1).trimmed().toUInt(); 01029 else if (item.startsWith("timing_port")) 01030 timing_port = item.mid(item.indexOf("=") + 1).trimmed().toUInt(); 01031 } 01032 01033 LOG(VB_GENERAL, LOG_INFO, LOC + 01034 QString("Negotiated setup with client %1 on port %2") 01035 .arg(m_socket->peerAddress().toString()) 01036 .arg(m_socket->peerPort())); 01037 LOG(VB_GENERAL, LOG_DEBUG, LOC + 01038 QString("control port: %1 timing port: %2") 01039 .arg(control_port).arg(timing_port)); 01040 01041 m_peerAddress = m_socket->peerAddress(); 01042 01043 if (m_clientControlSocket) 01044 { 01045 m_clientControlSocket->disconnect(); 01046 m_clientControlSocket->close(); 01047 delete m_clientControlSocket; 01048 } 01049 01050 m_clientControlSocket = new ServerPool(this); 01051 int controlbind_port = 01052 m_clientControlSocket->tryBindingPort(control_port, 01053 RAOP_PORT_RANGE); 01054 if (controlbind_port < 0) 01055 { 01056 LOG(VB_GENERAL, LOG_ERR, LOC + 01057 QString("Failed to bind to client control port. " 01058 "Control of audio stream may fail")); 01059 } 01060 else 01061 { 01062 LOG(VB_GENERAL, LOG_INFO, LOC + 01063 QString("Bound to client control port %1 on port %2") 01064 .arg(control_port).arg(controlbind_port)); 01065 } 01066 m_clientControlPort = control_port; 01067 connect(m_clientControlSocket, 01068 SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)), 01069 this, 01070 SLOT(udpDataReady(QByteArray, QHostAddress, quint16))); 01071 01072 if (m_clientTimingSocket) 01073 { 01074 m_clientTimingSocket->disconnect(); 01075 m_clientTimingSocket->close(); 01076 delete m_clientTimingSocket; 01077 } 01078 01079 m_clientTimingSocket = new ServerPool(this); 01080 int timingbind_port = 01081 m_clientTimingSocket->tryBindingPort(timing_port, 01082 RAOP_PORT_RANGE); 01083 if (timingbind_port < 0) 01084 { 01085 LOG(VB_GENERAL, LOG_ERR, LOC + 01086 QString("Failed to bind to client timing port. " 01087 "Timing of audio stream will be incorrect")); 01088 } 01089 else 01090 { 01091 LOG(VB_GENERAL, LOG_INFO, LOC + 01092 QString("Bound to client timing port %1 on port %2") 01093 .arg(timing_port).arg(timingbind_port)); 01094 } 01095 m_clientTimingPort = timing_port; 01096 connect(m_clientTimingSocket, 01097 SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)), 01098 this, 01099 SLOT(udpDataReady(QByteArray, QHostAddress, quint16))); 01100 01101 if (OpenAudioDevice()) 01102 CreateDecoder(); 01103 01104 // Recreate transport line with new ports value 01105 QString newdata; 01106 bool first = true; 01107 foreach (QString item, items) 01108 { 01109 if (!first) 01110 { 01111 newdata += ";"; 01112 } 01113 if (item.startsWith("control_port")) 01114 { 01115 newdata += "control_port=" + QString::number(controlbind_port); 01116 } 01117 else if (item.startsWith("timing_port")) 01118 { 01119 newdata += "timing_port=" + QString::number(timingbind_port); 01120 } 01121 else 01122 { 01123 newdata += item; 01124 } 01125 first = false; 01126 } 01127 if (!first) 01128 { 01129 newdata += ";"; 01130 } 01131 newdata += "server_port=" + QString::number(m_dataPort); 01132 01133 StartResponse(m_textStream, option, tags["CSeq"]); 01134 *m_textStream << "Transport: " << newdata; 01135 *m_textStream << "\r\nSession: MYTHTV\r\n"; 01136 } 01137 else 01138 { 01139 LOG(VB_GENERAL, LOG_ERR, LOC + 01140 "No Transport details found - Ignoring"); 01141 } 01142 } 01143 else if (option == "RECORD") 01144 { 01145 if (gotRTP) 01146 { 01147 m_nextSequence = RTPseq; 01148 m_nextTimestamp = RTPtimestamp; 01149 } 01150 // Ask for master clock value to determine time skew and average network latency 01151 SendTimeRequest(); 01152 StartResponse(m_textStream, option, tags["CSeq"]); 01153 } 01154 else if (option == "FLUSH") 01155 { 01156 if (gotRTP) 01157 { 01158 m_nextSequence = RTPseq; 01159 m_nextTimestamp = RTPtimestamp; 01160 m_currentTimestamp = m_nextTimestamp - m_bufferLength; 01161 } 01162 // determine RTP timestamp of last sample played 01163 uint64_t timestamp = m_audioStarted && m_audio ? 01164 m_audio->GetAudiotime() : m_lastTimestamp; 01165 *m_textStream << "RTP-Info: rtptime=" << QString::number(timestamp); 01166 m_streamingStarted = false; 01167 ResetAudio(); 01168 StartResponse(m_textStream, option, tags["CSeq"]); 01169 } 01170 else if (option == "SET_PARAMETER") 01171 { 01172 if (tags.contains("Content-Type")) 01173 { 01174 if (tags["Content-Type"] == "text/parameters") 01175 { 01176 QString name = content.left(content.indexOf(":")); 01177 QString param = content.mid(content.indexOf(":") + 1).trimmed(); 01178 01179 LOG(VB_GENERAL, LOG_DEBUG, LOC + 01180 QString("text/parameters: name=%1 parem=%2") 01181 .arg(name).arg(param)); 01182 01183 if (name == "volume" && m_allowVolumeControl && m_audio) 01184 { 01185 float volume = (param.toFloat() + 30.0f) * 100.0f / 30.0f; 01186 if (volume < 0.01f) 01187 volume = 0.0f; 01188 LOG(VB_GENERAL, LOG_INFO, 01189 LOC + QString("Setting volume to %1 (raw %3)") 01190 .arg(volume).arg(param)); 01191 m_audio->SetCurrentVolume((int)volume); 01192 } 01193 else if (name == "progress") 01194 { 01195 QStringList items = param.split("/"); 01196 if (items.size() == 3) 01197 { 01198 m_progressStart = items[0].toUInt(); 01199 m_progressCurrent = items[1].toUInt(); 01200 m_progressEnd = items[2].toUInt(); 01201 } 01202 int length = 01203 (m_progressEnd-m_progressStart) / m_frameRate; 01204 int current = 01205 (m_progressCurrent-m_progressStart) / m_frameRate; 01206 01207 LOG(VB_GENERAL, LOG_INFO, 01208 LOC +QString("Progress: %1/%2") 01209 .arg(stringFromSeconds(current)) 01210 .arg(stringFromSeconds(length))); 01211 } 01212 } 01213 else if(tags["Content-Type"] == "image/jpeg") 01214 { 01215 // Receiving image coverart 01216 m_artwork = content; 01217 } 01218 else if (tags["Content-Type"] == "application/x-dmap-tagged") 01219 { 01220 // Receiving DMAP metadata 01221 QMap<QString,QString> map = decodeDMAP(content); 01222 LOG(VB_GENERAL, LOG_INFO, 01223 QString("Receiving Title:%1 Artist:%2 Album:%3 Format:%4") 01224 .arg(map["minm"]).arg(map["asar"]) 01225 .arg(map["asal"]).arg(map["asfm"])); 01226 } 01227 } 01228 StartResponse(m_textStream, option, tags["CSeq"]); 01229 } 01230 else if (option == "TEARDOWN") 01231 { 01232 StartResponse(m_textStream, option, tags["CSeq"]); 01233 *m_textStream << "Connection: close\r\n"; 01234 } 01235 else 01236 { 01237 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Command not handled: %1") 01238 .arg(option)); 01239 StartResponse(m_textStream, option, tags["CSeq"]); 01240 } 01241 FinishResponse(m_textStream, m_socket, option, tags["CSeq"]); 01242 } 01243 01244 01245 void MythRAOPConnection::StartResponse(NetStream *stream, 01246 QString &option, QString &cseq) 01247 { 01248 if (!stream) 01249 return; 01250 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("%1 sequence %2") 01251 .arg(option).arg(cseq)); 01252 *stream << "Audio-Jack-Status: connected; type=analog\r\n"; 01253 *stream << "CSeq: " << cseq << "\r\n"; 01254 } 01255 01256 void MythRAOPConnection::FinishResponse(NetStream *stream, QTcpSocket *socket, 01257 QString &option, QString &cseq) 01258 { 01259 *stream << "\r\n"; 01260 stream->flush(); 01261 LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Finished %1 %2 , Send: %3") 01262 .arg(option).arg(cseq).arg(socket->flush())); 01263 } 01264 01270 RSA* MythRAOPConnection::LoadKey(void) 01271 { 01272 static QMutex lock; 01273 QMutexLocker locker(&lock); 01274 01275 if (g_rsa) 01276 return g_rsa; 01277 01278 QString sName( "/RAOPKey.rsa" ); 01279 FILE * file = fopen(GetConfDir().toUtf8() + sName.toUtf8(), "rb"); 01280 01281 if ( !file ) 01282 { 01283 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to read key from: %1") 01284 .arg(GetConfDir() + sName)); 01285 g_rsa = NULL; 01286 return NULL; 01287 } 01288 01289 g_rsa = PEM_read_RSAPrivateKey(file, NULL, NULL, NULL); 01290 fclose(file); 01291 01292 if (g_rsa) 01293 { 01294 LOG(VB_GENERAL, LOG_DEBUG, LOC + 01295 QString("Loaded RSA private key (%1)").arg(RSA_check_key(g_rsa))); 01296 return g_rsa; 01297 } 01298 01299 g_rsa = NULL; 01300 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to load RSA private key."); 01301 return NULL; 01302 } 01303 01304 RawHash MythRAOPConnection::FindTags(const QStringList &lines) 01305 { 01306 RawHash result; 01307 if (lines.isEmpty()) 01308 return result; 01309 01310 for (int i = 0; i < lines.size(); i++) 01311 { 01312 int index = lines[i].indexOf(":"); 01313 if (index > 0) 01314 { 01315 result.insert(lines[i].left(index).trimmed(), 01316 lines[i].mid(index + 1).trimmed()); 01317 } 01318 } 01319 return result; 01320 } 01321 01322 QStringList MythRAOPConnection::splitLines(const QByteArray &lines) 01323 { 01324 QStringList list; 01325 QTextStream stream(lines); 01326 01327 QString line; 01328 do 01329 { 01330 line = stream.readLine(); 01331 if (!line.isNull()) 01332 { 01333 list.append(line); 01334 } 01335 } 01336 while (!line.isNull()); 01337 01338 return list; 01339 } 01340 01348 QString MythRAOPConnection::stringFromSeconds(int time) 01349 { 01350 int hour = time / 3600; 01351 int minute = (time - hour * 3600) / 60; 01352 int seconds = time - hour * 3600 - minute * 60; 01353 QString str; 01354 01355 if (hour) 01356 { 01357 str += QString("%1:").arg(hour); 01358 } 01359 if (minute < 10) 01360 { 01361 str += "0"; 01362 } 01363 str += QString("%1:").arg(minute); 01364 if (seconds < 10) 01365 { 01366 str += "0"; 01367 } 01368 str += QString::number(seconds); 01369 return str; 01370 } 01371 01377 uint64_t MythRAOPConnection::framesToMs(uint64_t frames) 01378 { 01379 return (frames * 1000ULL) / m_frameRate; 01380 } 01381 01389 QMap<QString,QString> MythRAOPConnection::decodeDMAP(const QByteArray &dmap) 01390 { 01391 QMap<QString,QString> result; 01392 int offset = 8; 01393 while (offset < dmap.size()) 01394 { 01395 QString tag = dmap.mid(offset, 4); 01396 offset += 4; 01397 uint32_t length = qFromBigEndian(*(uint32_t *)(dmap.constData() + offset)); 01398 offset += sizeof(uint32_t); 01399 QString content = QString::fromUtf8(dmap.mid(offset, 01400 length).constData()); 01401 offset += length; 01402 result.insert(tag, content); 01403 } 01404 return result; 01405 } 01406 01407 bool MythRAOPConnection::CreateDecoder(void) 01408 { 01409 DestroyDecoder(); 01410 01411 // create an ALAC decoder 01412 avcodeclock->lock(); 01413 av_register_all(); 01414 avcodeclock->unlock(); 01415 01416 m_codec = avcodec_find_decoder(CODEC_ID_ALAC); 01417 if (!m_codec) 01418 { 01419 LOG(VB_GENERAL, LOG_ERR, LOC 01420 + "Failed to create ALAC decoder- going silent..."); 01421 return false; 01422 } 01423 01424 m_codeccontext = avcodec_alloc_context3(m_codec); 01425 if (m_codeccontext) 01426 { 01427 unsigned char* extradata = new unsigned char[36]; 01428 memset(extradata, 0, 36); 01429 if (m_audioFormat.size() < 12) 01430 { 01431 LOG(VB_GENERAL, LOG_ERR, LOC + 01432 "Creating decoder but haven't seen audio format."); 01433 } 01434 else 01435 { 01436 uint32_t fs = m_audioFormat[1]; // frame size 01437 extradata[12] = (fs >> 24) & 0xff; 01438 extradata[13] = (fs >> 16) & 0xff; 01439 extradata[14] = (fs >> 8) & 0xff; 01440 extradata[15] = fs & 0xff; 01441 extradata[16] = m_channels; // channels 01442 extradata[17] = m_audioFormat[3]; // sample size 01443 extradata[18] = m_audioFormat[4]; // rice_historymult 01444 extradata[19] = m_audioFormat[5]; // rice_initialhistory 01445 extradata[20] = m_audioFormat[6]; // rice_kmodifier 01446 } 01447 m_codeccontext->extradata = extradata; 01448 m_codeccontext->extradata_size = 36; 01449 m_codeccontext->channels = m_channels; 01450 if (avcodec_open2(m_codeccontext, m_codec, NULL) < 0) 01451 { 01452 LOG(VB_GENERAL, LOG_ERR, LOC + 01453 "Failed to open ALAC decoder - going silent..."); 01454 DestroyDecoder(); 01455 return false; 01456 } 01457 LOG(VB_GENERAL, LOG_DEBUG, LOC + "Opened ALAC decoder."); 01458 } 01459 01460 return true; 01461 } 01462 01463 void MythRAOPConnection::DestroyDecoder(void) 01464 { 01465 if (m_codeccontext) 01466 { 01467 avcodec_close(m_codeccontext); 01468 av_free(m_codeccontext); 01469 } 01470 m_codec = NULL; 01471 m_codeccontext = NULL; 01472 } 01473 01474 bool MythRAOPConnection::OpenAudioDevice(void) 01475 { 01476 CloseAudioDevice(); 01477 01478 QString passthru = gCoreContext->GetNumSetting("PassThruDeviceOverride", false) 01479 ? gCoreContext->GetSetting("PassThruOutputDevice") : QString::null; 01480 QString device = gCoreContext->GetSetting("AudioOutputDevice"); 01481 01482 m_audio = AudioOutput::OpenAudio(device, passthru, FORMAT_S16, m_channels, 01483 0, m_frameRate, AUDIOOUTPUT_MUSIC, 01484 m_allowVolumeControl, false); 01485 if (!m_audio) 01486 { 01487 LOG(VB_GENERAL, LOG_ERR, LOC + 01488 "Failed to open audio device. Going silent..."); 01489 CloseAudioDevice(); 01490 StartAudioTimer(); 01491 return false; 01492 } 01493 01494 QString error = m_audio->GetError(); 01495 if (!error.isEmpty()) 01496 { 01497 LOG(VB_GENERAL, LOG_ERR, LOC + 01498 QString("Audio not initialised. Message was '%1'") 01499 .arg(error)); 01500 CloseAudioDevice(); 01501 StartAudioTimer(); 01502 return false; 01503 } 01504 01505 StopAudioTimer(); 01506 LOG(VB_GENERAL, LOG_DEBUG, LOC + "Opened audio device."); 01507 return true; 01508 } 01509 01510 void MythRAOPConnection::CloseAudioDevice(void) 01511 { 01512 delete m_audio; 01513 m_audio = NULL; 01514 } 01515 01516 void MythRAOPConnection::StartAudioTimer(void) 01517 { 01518 if (m_audioTimer) 01519 return; 01520 01521 m_audioTimer = new QTimer(); 01522 connect(m_audioTimer, SIGNAL(timeout()), this, SLOT(audioRetry())); 01523 m_audioTimer->start(5000); 01524 } 01525 01526 void MythRAOPConnection::StopAudioTimer(void) 01527 { 01528 if (m_audioTimer) 01529 { 01530 m_audioTimer->stop(); 01531 } 01532 delete m_audioTimer; 01533 m_audioTimer = NULL; 01534 } 01535 01540 int64_t MythRAOPConnection::AudioCardLatency(void) 01541 { 01542 if (!m_audio) 01543 return 0; 01544 01545 uint64_t timestamp = 123456; 01546 01547 int16_t *samples = (int16_t *)av_mallocz(AVCODEC_MAX_AUDIO_FRAME_SIZE); 01548 int frames = AUDIOCARD_BUFFER * m_frameRate / 1000; 01549 m_audio->AddData((char *)samples, 01550 frames * (m_sampleSize>>3) * m_channels, 01551 timestamp, 01552 frames); 01553 av_free(samples); 01554 usleep(AUDIOCARD_BUFFER * 1000); 01555 uint64_t audiots = m_audio->GetAudiotime(); 01556 return (int64_t)timestamp - (int64_t)audiots; 01557 }
1.7.6.1