From bace8ebdaae2e8ecc503b2259cabfacfaab7165b Mon Sep 17 00:00:00 2001 From: Paul Harrison Date: Sun, 14 Jul 2013 22:58:23 +0100 Subject: [PATCH 1/5] MythMusic: Remove an incorrect apostrophe added in fe97815b9 --- mythplugins/mythmusic/mythmusic/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mythplugins/mythmusic/mythmusic/main.cpp b/mythplugins/mythmusic/mythmusic/main.cpp index 57b0ab9876c..c0adc2fb50f 100644 --- a/mythplugins/mythmusic/mythmusic/main.cpp +++ b/mythplugins/mythmusic/mythmusic/main.cpp @@ -196,9 +196,9 @@ static void startRipper(void) delete rip; #else - ShowOkPopup(qApp->translate("(MythMusicMain)", + ShowOkPopup(qApp->translate("(MythMusicMain)", "MythMusic hasn't been built with libcdio " - "support so ripping CD's is not possible")); + "support so ripping CDs is not possible")); #endif } From c95619a22c8a6f73bef5710a3f413c4e694f1301 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 15 Jul 2013 04:43:20 +1000 Subject: [PATCH 2/5] Change log type for RAOP --- .../libmythtv/AirPlay/mythraopconnection.cpp | 142 +++++++++--------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/mythtv/libs/libmythtv/AirPlay/mythraopconnection.cpp b/mythtv/libs/libmythtv/AirPlay/mythraopconnection.cpp index 7b664351ec5..cbafbdb3e08 100644 --- a/mythtv/libs/libmythtv/AirPlay/mythraopconnection.cpp +++ b/mythtv/libs/libmythtv/AirPlay/mythraopconnection.cpp @@ -49,7 +49,7 @@ class _NetStream : public QTextStream }; _NetStream &operator<<(const QString &str) { - LOG(VB_GENERAL, LOG_DEBUG, + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Sending(%1): ").arg(str.length()) + str); QTextStream *q = this; *q << str; @@ -196,7 +196,7 @@ bool MythRAOPConnection::Init(void) m_textStream->setCodec("UTF-8"); if (!connect(m_socket, SIGNAL(readyRead()), this, SLOT(readClient()))) { - LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect client socket signal."); + LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to connect client socket signal."); return false; } @@ -205,7 +205,7 @@ bool MythRAOPConnection::Init(void) if (!connect(m_dataSocket, SIGNAL(newDatagram(QByteArray, QHostAddress, quint16)), this, SLOT(udpDataReady(QByteArray, QHostAddress, quint16)))) { - LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to connect data socket signal."); + LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to connect data socket signal."); return false; } @@ -213,11 +213,11 @@ bool MythRAOPConnection::Init(void) m_dataPort = m_dataSocket->tryBindingPort(m_dataPort, RAOP_PORT_RANGE); if (m_dataPort < 0) { - LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to bind to a port for data."); + LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to bind to a port for data."); return false; } - LOG(VB_GENERAL, LOG_INFO, LOC + + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Bound to port %1 for incoming data").arg(m_dataPort)); // load the private key @@ -258,7 +258,7 @@ void MythRAOPConnection::udpDataReady(QByteArray buf, QHostAddress peer, if (!GetPacketType(buf, type, seq, timestamp)) { - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Packet doesn't start with valid Rtp Header (0x%1)") .arg((uint8_t)buf[0], 0, 16)); return; @@ -290,7 +290,7 @@ void MythRAOPConnection::udpDataReady(QByteArray buf, QHostAddress peer, return; default: - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Packet type (0x%1) not handled") .arg(type, 0, 16)); return; @@ -299,7 +299,7 @@ void MythRAOPConnection::udpDataReady(QByteArray buf, QHostAddress peer, timestamp = framesToMs(timestamp); if (timestamp < m_currentTimestamp) { - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Received packet %1 too late, ignoring") .arg(seq)); return; @@ -323,13 +323,13 @@ void MythRAOPConnection::udpDataReady(QByteArray buf, QHostAddress peer, { if (m_resends.contains(seq)) { - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Received required resend %1 (with ts:%2 last:%3)") .arg(seq).arg(timestamp).arg(m_nextSequence)); m_resends.remove(seq); } else - LOG(VB_GENERAL, LOG_WARNING, LOC + + LOG(VB_PLAYBACK, LOG_WARNING, LOC + QString("Received unexpected resent packet %1") .arg(seq)); } @@ -341,7 +341,7 @@ void MythRAOPConnection::udpDataReady(QByteArray buf, QHostAddress peer, if (numframes < 0) { // an error occurred, ask for the audio packet once again. - LOG(VB_GENERAL, LOG_ERR, LOC + QString("Error decoding audio")); + LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Error decoding audio")); SendResendRequest(timestamp, seq, seq+1); return; } @@ -374,17 +374,17 @@ void MythRAOPConnection::ProcessSync(const QByteArray &buf) if (first) { - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Receiving first SYNC packet")); + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Receiving first SYNC packet")); } else { - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Receiving SYNC packet")); + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Receiving SYNC packet")); } timeval t; gettimeofday(&t, NULL); m_timeLastSync = t.tv_sec * 1000 + t.tv_usec / 1000; - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("SYNC: cur:%1 next:%2 time:%3") + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("SYNC: cur:%1 next:%2 time:%3") .arg(m_currentTimestamp).arg(m_nextTimestamp).arg(m_timeLastSync)); int64_t delay = framesToMs(m_audioQueue.size() * m_framesPerPacket); @@ -396,7 +396,7 @@ void MythRAOPConnection::ProcessSync(const QByteArray &buf) currentLatency = (int64_t)audiots - (int64_t)m_currentTimestamp; } - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("RAOP timestamps: about to play:%1 desired:%2 latency:%3") .arg(audiots).arg(m_currentTimestamp) .arg(currentLatency)); @@ -404,7 +404,7 @@ void MythRAOPConnection::ProcessSync(const QByteArray &buf) delay += m_audio->GetAudioBufferedTime(); delay += currentLatency; - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Queue=%1 buffer=%2ms ideal=%3ms diffts:%4ms") .arg(m_audioQueue.size()) .arg(delay) @@ -430,7 +430,7 @@ void MythRAOPConnection::ProcessSync(const QByteArray &buf) int res = ExpireAudio(m_currentTimestamp - m_adjustedLatency); if (res > 0) { - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Drop %1 packets").arg(res)); + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Drop %1 packets").arg(res)); } m_audioStarted = false; @@ -451,7 +451,7 @@ void MythRAOPConnection::SendResendRequest(uint64_t timestamp, (int16_t)(((int32_t)got + UINT16_MAX + 1) - expected) : got - expected; - LOG(VB_GENERAL, LOG_INFO, LOC + + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Missed %1 packet(s): expected %2 got %3 ts:%4") .arg(missed).arg(expected).arg(got).arg(timestamp)); @@ -468,13 +468,13 @@ void MythRAOPConnection::SendResendRequest(uint64_t timestamp, { for (uint16_t count = 0; count < missed; count++) { - LOG(VB_GENERAL, LOG_INFO, LOC + QString("Sent resend for %1") + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Sent resend for %1") .arg(expected + count)); m_resends.insert(expected + count, timestamp); } } else - LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to send resend request."); + LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to send resend request."); } /** @@ -493,7 +493,7 @@ void MythRAOPConnection::ExpireResendRequests(uint64_t timestamp) it.next(); if (it.value() < timestamp && m_streamingStarted) { - LOG(VB_GENERAL, LOG_WARNING, LOC + + LOG(VB_PLAYBACK, LOG_WARNING, LOC + QString("Never received resend packet %1").arg(it.key())); m_resends.remove(it.key()); } @@ -527,10 +527,10 @@ void MythRAOPConnection::SendTimeRequest(void) if (m_clientTimingSocket->writeDatagram(req, sizeof(req), m_peerAddress, m_clientTimingPort) != sizeof(req)) { - LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to send resend time request."); + LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to send resend time request."); return; } - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Requesting master time (Local %1.%2)") .arg(t.tv_sec).arg(t.tv_usec)); } @@ -553,7 +553,7 @@ void MythRAOPConnection::ProcessTimeResponse(const QByteArray &buf) uint64_t time1, time2; time1 = t1.tv_sec * 1000 + t1.tv_usec / 1000; time2 = t2.tv_sec * 1000 + t2.tv_usec / 1000; - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Read back time (Local %1.%2)") + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Read back time (Local %1.%2)") .arg(t1.tv_sec).arg(t1.tv_usec)); // network latency equal time difference in ms between request and response // divide by two for approximate time of one way trip @@ -724,7 +724,7 @@ void MythRAOPConnection::ProcessAudio() if (m_lastSequence != frames.seq) { - LOG(VB_GENERAL, LOG_ERR, LOC + + LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Audio discontinuity seen. Played %1 (%3) expected %2") .arg(frames.seq).arg(m_lastSequence).arg(timestamp)); m_lastSequence = frames.seq; @@ -812,7 +812,7 @@ void MythRAOPConnection::ResetAudio(void) void MythRAOPConnection::timeout(void) { - LOG(VB_GENERAL, LOG_INFO, LOC + "Closing connection after inactivity."); + LOG(VB_PLAYBACK, LOG_INFO, LOC + "Closing connection after inactivity."); m_socket->disconnectFromHost(); } @@ -840,7 +840,7 @@ void MythRAOPConnection::readClient(void) return; QByteArray data = socket->readAll(); - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("readClient(%1): ") + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("readClient(%1): ") .arg(data.size()) + data.constData()); // For big content, we may be called several times for a single packet @@ -857,7 +857,7 @@ void MythRAOPConnection::readClient(void) line = stream.readLine(); if (line.size() == 0) break; - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Header(%1) = %2") + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Header(%1) = %2") .arg(m_socket->peerAddress().toString()) .arg(line)); m_incomingHeaders.append(line); @@ -896,7 +896,7 @@ void MythRAOPConnection::readClient(void) { m_incomingPartial = false; } - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Content(%1) = %2") + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Content(%1) = %2") .arg(m_incomingContent.size()).arg(m_incomingContent.constData())); ProcessRequest(m_incomingHeaders, m_incomingContent); @@ -912,7 +912,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, if (!tags.contains("CSeq")) { - LOG(VB_GENERAL, LOG_ERR, LOC + "ProcessRequest: Didn't find CSeq"); + LOG(VB_PLAYBACK, LOG_ERR, LOC + "ProcessRequest: Didn't find CSeq"); return; } @@ -938,7 +938,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, RTPtimestamp = item.mid(item.indexOf("=") + 1).trimmed().toUInt(); } } - LOG(VB_GENERAL, LOG_INFO, LOC + QString("RTP-Info: seq=%1 rtptime=%2") + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("RTP-Info: seq=%1 rtptime=%2") .arg(RTPseq).arg(RTPtimestamp)); } @@ -961,11 +961,11 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, gCoreContext->GetSetting("AirPlayPassword"), auth) == auth) { - LOG(VB_GENERAL, LOG_INFO, LOC + "RAOP client authenticated"); + LOG(VB_PLAYBACK, LOG_INFO, LOC + "RAOP client authenticated"); } else { - LOG(VB_GENERAL, LOG_INFO, LOC + "RAOP authentication failed"); + LOG(VB_PLAYBACK, LOG_INFO, LOC + "RAOP authentication failed"); FinishAuthenticationResponse(m_textStream, m_socket, tags["CSeq"]); return; } @@ -974,7 +974,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, if (tags.contains("Apple-Challenge")) { - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Received Apple-Challenge")); + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Received Apple-Challenge")); *m_textStream << "Apple-Response: "; if (!LoadKey()) @@ -987,7 +987,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, int challenge_size = challenge.size(); if (challenge_size != 16) { - LOG(VB_GENERAL, LOG_ERR, LOC + + LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Decoded challenge size %1, expected 16") .arg(challenge_size)); if (challenge_size > 16) @@ -1033,7 +1033,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, i += pad; } - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Full base64 response: '%1' size %2") .arg(QByteArray((const char *)from, i).toBase64().constData()) .arg(i)); @@ -1050,7 +1050,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, else break; } - LOG(VB_GENERAL, LOG_DEBUG, QString("tSize=%1 tLen=%2 tResponse=%3") + LOG(VB_PLAYBACK, LOG_DEBUG, QString("tSize=%1 tLen=%2 tResponse=%3") .arg(tosize).arg(base64.size()).arg(base64.constData())); *m_textStream << base64.trimmed() << "\r\n"; } @@ -1069,7 +1069,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, { QString key = line.mid(12).trimmed(); QByteArray decodedkey = QByteArray::fromBase64(key.toLatin1()); - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("RSAAESKey: %1 (decoded size %2)") .arg(key).arg(decodedkey.size())); @@ -1082,14 +1082,14 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, (unsigned char *)decryptedkey, LoadKey(), RSA_PKCS1_OAEP_PADDING)) { - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "Successfully decrypted AES key from RSA."); AES_set_decrypt_key((const unsigned char *)decryptedkey, 128, &m_aesKey); } else { - LOG(VB_GENERAL, LOG_WARNING, LOC + + LOG(VB_PLAYBACK, LOG_WARNING, LOC + "Failed to decrypt AES key from RSA."); } delete [] decryptedkey; @@ -1099,7 +1099,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, { QString aesiv = line.mid(8).trimmed(); m_AESIV = QByteArray::fromBase64(aesiv.toLatin1()); - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("AESIV: %1 (decoded size %2)") .arg(aesiv).arg(m_AESIV.size())); } @@ -1112,7 +1112,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, m_audioFormat.append(fmt.toInt()); foreach (int fmt, m_audioFormat) - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Audio parameter: %1").arg(fmt)); m_framesPerPacket = m_audioFormat[1]; m_sampleSize = m_audioFormat[3]; @@ -1146,11 +1146,11 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, events = true; } - LOG(VB_GENERAL, LOG_INFO, LOC + + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Negotiated setup with client %1 on port %2") .arg(m_socket->peerAddress().toString()) .arg(m_socket->peerPort())); - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("control port: %1 timing port: %2") .arg(control_port).arg(timing_port)); @@ -1169,13 +1169,13 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, RAOP_PORT_RANGE); if (controlbind_port < 0) { - LOG(VB_GENERAL, LOG_ERR, LOC + + LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Failed to bind to client control port. " "Control of audio stream may fail")); } else { - LOG(VB_GENERAL, LOG_INFO, LOC + + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Bound to client control port %1 on port %2") .arg(control_port).arg(controlbind_port)); } @@ -1198,13 +1198,13 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, RAOP_PORT_RANGE); if (timingbind_port < 0) { - LOG(VB_GENERAL, LOG_ERR, LOC + + LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Failed to bind to client timing port. " "Timing of audio stream will be incorrect")); } else { - LOG(VB_GENERAL, LOG_INFO, LOC + + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Bound to client timing port %1 on port %2") .arg(timing_port).arg(timingbind_port)); } @@ -1238,12 +1238,12 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, RAOP_PORT_RANGE); if (m_eventPort < 0) { - LOG(VB_GENERAL, LOG_ERR, LOC + + LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to find a port for RAOP events server."); } else { - LOG(VB_GENERAL, LOG_INFO, LOC + + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Listening for RAOP events on port %1").arg(m_eventPort)); connect(m_eventServer, SIGNAL(newConnection(QTcpSocket *)), this, SLOT(newEventClient(QTcpSocket *))); @@ -1259,7 +1259,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, m_adjustedLatency = m_cardLatency = AudioCardLatency(); // if audio isn't started, start playing 500ms worth of silence // and measure timestamp difference - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Audio hardware latency: %1ms") .arg(m_cardLatency + m_networkLatency)); } @@ -1307,7 +1307,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, } else { - LOG(VB_GENERAL, LOG_ERR, LOC + + LOG(VB_PLAYBACK, LOG_ERR, LOC + "No Transport details found - Ignoring"); } } @@ -1350,7 +1350,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, QString name = content.left(content.indexOf(":")); QString param = content.mid(content.indexOf(":") + 1).trimmed(); - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("text/parameters: name=%1 parem=%2") .arg(name).arg(param)); @@ -1359,7 +1359,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, float volume = (param.toFloat() + 30.0f) * 100.0f / 30.0f; if (volume < 0.01f) volume = 0.0f; - LOG(VB_GENERAL, LOG_INFO, + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Setting volume to %1 (raw %3)") .arg(volume).arg(param)); m_audio->SetCurrentVolume((int)volume); @@ -1378,7 +1378,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, int current = (m_progressCurrent-m_progressStart) / m_frameRate; - LOG(VB_GENERAL, LOG_INFO, + LOG(VB_PLAYBACK, LOG_INFO, LOC +QString("Progress: %1/%2") .arg(stringFromSeconds(current)) .arg(stringFromSeconds(length))); @@ -1400,7 +1400,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, { // Receiving DMAP metadata m_dmap = decodeDMAP(content); - LOG(VB_GENERAL, LOG_INFO, + LOG(VB_PLAYBACK, LOG_INFO, QString("Receiving Title:%1 Artist:%2 Album:%3 Format:%4") .arg(m_dmap["minm"]).arg(m_dmap["asar"]) .arg(m_dmap["asal"]).arg(m_dmap["asfm"])); @@ -1415,7 +1415,7 @@ void MythRAOPConnection::ProcessRequest(const QStringList &header, } else { - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Command not handled: %1") + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Command not handled: %1") .arg(option)); } FinishResponse(m_textStream, m_socket, option, tags["CSeq"]); @@ -1434,7 +1434,7 @@ void MythRAOPConnection::FinishAuthenticationResponse(_NetStream *stream, *stream << "CSeq: " << cseq << "\r\n"; *stream << "\r\n"; stream->flush(); - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Finished Authentication request %2, Send: %3") .arg(cseq).arg(socket->flush())); } @@ -1448,7 +1448,7 @@ void MythRAOPConnection::FinishResponse(_NetStream *stream, QTcpSocket *socket, *stream << "CSeq: " << cseq << "\r\n"; *stream << "\r\n"; stream->flush(); - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Finished %1 %2 , Send: %3") + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Finished %1 %2 , Send: %3") .arg(option).arg(cseq).arg(socket->flush())); } @@ -1472,7 +1472,7 @@ RSA *MythRAOPConnection::LoadKey(void) { g_rsaLastError = QObject::tr("Failed to read key from: %1").arg(GetConfDir() + sName); g_rsa = NULL; - LOG(VB_GENERAL, LOG_ERR, LOC + g_rsaLastError); + LOG(VB_PLAYBACK, LOG_ERR, LOC + g_rsaLastError); return NULL; } @@ -1482,14 +1482,14 @@ RSA *MythRAOPConnection::LoadKey(void) if (g_rsa) { g_rsaLastError = ""; - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("Loaded RSA private key (%1)").arg(RSA_check_key(g_rsa))); return g_rsa; } g_rsaLastError = QObject::tr("Failed to load RSA private key."); g_rsa = NULL; - LOG(VB_GENERAL, LOG_ERR, LOC + g_rsaLastError); + LOG(VB_PLAYBACK, LOG_ERR, LOC + g_rsaLastError); return NULL; } @@ -1608,7 +1608,7 @@ bool MythRAOPConnection::CreateDecoder(void) m_codec = avcodec_find_decoder(CODEC_ID_ALAC); if (!m_codec) { - LOG(VB_GENERAL, LOG_ERR, LOC + LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to create ALAC decoder- going silent..."); return false; } @@ -1620,7 +1620,7 @@ bool MythRAOPConnection::CreateDecoder(void) memset(extradata, 0, 36); if (m_audioFormat.size() < 12) { - LOG(VB_GENERAL, LOG_ERR, LOC + + LOG(VB_PLAYBACK, LOG_ERR, LOC + "Creating decoder but haven't seen audio format."); } else @@ -1641,12 +1641,12 @@ bool MythRAOPConnection::CreateDecoder(void) m_codeccontext->channels = m_channels; if (avcodec_open2(m_codeccontext, m_codec, NULL) < 0) { - LOG(VB_GENERAL, LOG_ERR, LOC + + LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to open ALAC decoder - going silent..."); DestroyDecoder(); return false; } - LOG(VB_GENERAL, LOG_DEBUG, LOC + "Opened ALAC decoder."); + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "Opened ALAC decoder."); } return true; @@ -1676,7 +1676,7 @@ bool MythRAOPConnection::OpenAudioDevice(void) m_allowVolumeControl, false); if (!m_audio) { - LOG(VB_GENERAL, LOG_ERR, LOC + + LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to open audio device. Going silent..."); CloseAudioDevice(); StartAudioTimer(); @@ -1686,7 +1686,7 @@ bool MythRAOPConnection::OpenAudioDevice(void) QString error = m_audio->GetError(); if (!error.isEmpty()) { - LOG(VB_GENERAL, LOG_ERR, LOC + + LOG(VB_PLAYBACK, LOG_ERR, LOC + QString("Audio not initialised. Message was '%1'") .arg(error)); CloseAudioDevice(); @@ -1695,7 +1695,7 @@ bool MythRAOPConnection::OpenAudioDevice(void) } StopAudioTimer(); - LOG(VB_GENERAL, LOG_DEBUG, LOC + "Opened audio device."); + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + "Opened audio device."); return true; } @@ -1750,7 +1750,7 @@ int64_t MythRAOPConnection::AudioCardLatency(void) void MythRAOPConnection::newEventClient(QTcpSocket *client) { - LOG(VB_GENERAL, LOG_INFO, LOC + + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("New connection from %1:%2 for RAOP events server.") .arg(client->peerAddress().toString()).arg(client->peerPort())); @@ -1763,7 +1763,7 @@ void MythRAOPConnection::deleteEventClient(void) { QTcpSocket *client = static_cast(sender()); - LOG(VB_GENERAL, LOG_DEBUG, LOC + + LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("%1:%2 disconnected from RAOP events server.") .arg(client->peerAddress().toString()).arg(client->peerPort())); } From 640da5a98241fe4210b545cbb9e28f6942f8b638 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 15 Jul 2013 12:44:02 +1000 Subject: [PATCH 3/5] Add multiple UI display dependencies support. Operators supported are & (AND) and | (OR); priority are strictly from left to right. If a dependee doesn't exist, it's considered as in unvisible state rather than just ignored --- mythtv/libs/libmythui/mythuitype.cpp | 111 +++++++++++++++++++++------ mythtv/libs/libmythui/mythuitype.h | 11 ++- 2 files changed, 98 insertions(+), 24 deletions(-) diff --git a/mythtv/libs/libmythui/mythuitype.cpp b/mythtv/libs/libmythui/mythuitype.cpp index 9accc4575d6..eab13db3ae1 100644 --- a/mythtv/libs/libmythui/mythuitype.cpp +++ b/mythtv/libs/libmythui/mythuitype.cpp @@ -47,7 +47,6 @@ MythUIType::MythUIType(QObject *parent, const QString &name) m_XYSpeed = QPoint(0, 0); m_deferload = false; m_IsDependDefault = false; - m_ReverseDepend = false; m_Parent = NULL; @@ -1037,13 +1036,52 @@ void MythUIType::Refresh(void) SetRedraw(); } -void MythUIType::UpdateDependState(bool isDefault) +void MythUIType::UpdateDependState(MythUIType *dependee, bool isDefault) { - m_IsDependDefault = m_ReverseDepend ? !isDefault : isDefault; + bool visible; + + if (dependee) + { + bool reverse = m_ReverseDepend[dependee]; + visible = reverse ? !isDefault : isDefault; + for (int i = 0; i < m_dependsValue.size(); i++) + { + if (m_dependsValue[i].first != dependee) + continue; + m_dependsValue[i].second = visible; + break; + } + } + + visible = m_dependsValue[0].second; + for (int i = 1; i < m_dependsValue.size(); i++) + { + bool v = m_dependsValue[i].second; + + if (m_dependOperator[i-1] == 1) + { + // OR operator + visible = visible && v; + } + else + { + // AND operator + visible = visible || v; + } + } + + m_IsDependDefault = visible; SetVisible(!m_IsDependDefault); } +void MythUIType::UpdateDependState(bool isDefault) +{ + MythUIType *dependee = static_cast(sender()); + + UpdateDependState(dependee, isDefault); +} + void MythUIType::SetVisible(bool visible) { if (visible == m_Visible) @@ -1159,7 +1197,6 @@ void MythUIType::CopyFrom(MythUIType *base) } m_dependsMap = base->m_dependsMap; - m_ReverseDepend = base->m_ReverseDepend; SetMinArea(base->m_MinArea); } @@ -1372,34 +1409,64 @@ void MythUIType::SetDependsMap(QMap dependsMap) m_dependsMap = dependsMap; } -void MythUIType::SetReverseDependence(bool reverse) +void MythUIType::SetReverseDependence(MythUIType *dependee, bool reverse) { - m_ReverseDepend = reverse; + m_ReverseDepend.insert(dependee, reverse); } void MythUIType::ConnectDependants(bool recurse) { - - QMapIterator i(m_dependsMap); - while(i.hasNext()) + QMapIterator it(m_dependsMap); + while(it.hasNext()) { - i.next(); - QString dependeeName = i.value(); - bool reverse = false; - if (dependeeName.startsWith('!')) + it.next(); + + // build list of operators and dependeees. + QStringList dependees; + QList operators; + QString name = it.value(); + QStringList tmp1 = name.split("&"); + for (int i = 0; i < tmp1.size(); i++) { - reverse = true; - dependeeName.remove(0,1); + QStringList tmp2 = tmp1[i].split("|"); + + dependees.append(tmp2[0]); + for (int j = 1; j < tmp2.size(); j++) + { + dependees.append(tmp2[j]); + operators.append(1); // 1 is OR + } + operators.append(2); // 2 is AND } - MythUIType *dependee = GetChild(dependeeName); - MythUIType *dependant = GetChild(i.key()); - if (dependee && dependant) + MythUIType *dependant = GetChild(it.key()); + if (dependant) { - QObject::connect(dependee, SIGNAL(DependChanged(bool)), - dependant, SLOT(UpdateDependState(bool))); - dependant->SetReverseDependence(reverse); - dependant->UpdateDependState(true); + dependant->m_dependOperator = operators; + foreach (QString dependeeName, dependees) + { + bool reverse = false; + if (dependeeName.startsWith('!')) + { + reverse = true; + dependeeName.remove(0,1); + } + MythUIType *dependee = GetChild(dependeeName); + + if (dependee) + { + QObject::connect(dependee, SIGNAL(DependChanged(bool)), + dependant, SLOT(UpdateDependState(bool))); + dependant->SetReverseDependence(dependee, reverse); + dependant->m_dependsValue.append(QPair(dependee, false)); + dependant->UpdateDependState(dependee, true); + } + else + { + dependant->m_dependsValue.append(QPair(dependee, !reverse)); + dependant->UpdateDependState(dependee, reverse); + } + } } } diff --git a/mythtv/libs/libmythui/mythuitype.h b/mythtv/libs/libmythui/mythuitype.h index 82ce7636aff..7c8d43803c6 100644 --- a/mythtv/libs/libmythui/mythuitype.h +++ b/mythtv/libs/libmythui/mythuitype.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -174,7 +175,7 @@ class MUI_PUBLIC MythUIType : public QObject, public XMLParseBase void SetVerticalZoom(float zoom); void SetAngle(float angle); void SetDependIsDefault(bool isDefault); - void SetReverseDependence(bool reverse); + void SetReverseDependence(MythUIType *dependee, bool reverse); void SetDependsMap(QMap dependsMap); QMap GetDependsMap() const { return m_dependsMap; } @@ -190,6 +191,7 @@ class MUI_PUBLIC MythUIType : public QObject, public XMLParseBase void Show(void); void Refresh(void); void UpdateDependState(bool isDefault); + void UpdateDependState(MythUIType *dependee, bool isDefault); signals: void RequestUpdate(); @@ -228,6 +230,11 @@ class MUI_PUBLIC MythUIType : public QObject, public XMLParseBase QList m_ChildrenList; QMap m_dependsMap; + // the number of dependencies is assumed to be small (1 or 2 elements on average) + // so we use a QList as we want the element ordered in the order they were defined + // and speed isn't going to be a factor + QList< QPair >m_dependsValue; + QList m_dependOperator; bool m_Visible; bool m_HasFocus; @@ -238,7 +245,7 @@ class MUI_PUBLIC MythUIType : public QObject, public XMLParseBase bool m_Vanish; bool m_Vanished; bool m_IsDependDefault; - bool m_ReverseDepend; + QMap m_ReverseDepend; int m_focusOrder; From 4d0fbb7fcda1b86f8478d09e2fc246c3234b9fc5 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 15 Jul 2013 12:48:35 +1000 Subject: [PATCH 4/5] Make no-artwork condition themeable. --- .../libmythui/mythuinotificationcenter.cpp | 29 +++++++++++++++++-- .../mythuinotificationcenter_private.h | 2 ++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/mythtv/libs/libmythui/mythuinotificationcenter.cpp b/mythtv/libs/libmythui/mythuinotificationcenter.cpp index b433765db30..c3815122e92 100644 --- a/mythtv/libs/libmythui/mythuinotificationcenter.cpp +++ b/mythtv/libs/libmythui/mythuinotificationcenter.cpp @@ -46,7 +46,8 @@ MythUINotificationScreen::MythUINotificationScreen(MythScreenStack *stack, m_created(false), m_content(kNone), m_update(kAll), m_artworkImage(NULL), m_titleText(NULL), m_originText(NULL), m_descriptionText(NULL), m_extraText(NULL), m_progresstextText(NULL), - m_progressBar(NULL), m_errorState(NULL), m_index(0), + m_progressBar(NULL), m_errorState(NULL), m_mediaState(NULL), + m_index(0), m_timer(new QTimer(this)), m_visibility(MythNotification::kAll), m_priority(MythNotification::kDefault) @@ -65,7 +66,8 @@ MythUINotificationScreen::MythUINotificationScreen(MythScreenStack *stack, m_update(kAll), m_artworkImage(NULL), m_titleText(NULL), m_originText(NULL), m_descriptionText(NULL), m_extraText(NULL), m_progresstextText(NULL), - m_progressBar(NULL), m_errorState(NULL), m_index(0), + m_progressBar(NULL), m_errorState(NULL), m_mediaState(NULL), + m_index(0), m_timer(new QTimer(this)), m_visibility(MythNotification::kAll), m_priority(MythNotification::kDefault) @@ -82,7 +84,7 @@ MythUINotificationScreen::MythUINotificationScreen(MythScreenStack *stack, m_created(false), m_content(kNone), m_update(kAll), m_artworkImage(NULL), m_titleText(NULL), m_originText(NULL), m_descriptionText(NULL), m_extraText(NULL), m_progresstextText(NULL), - m_progressBar(NULL), m_errorState(NULL), + m_progressBar(NULL), m_errorState(NULL), m_mediaState(NULL), m_timer(new QTimer(this)), m_visibility(MythNotification::kAll), m_priority(MythNotification::kDefault) @@ -140,6 +142,13 @@ void MythUINotificationScreen::SetNotification(MythNotification ¬ification) m_update |= kDuration; } + MythMediaNotification *media = + dynamic_cast(¬ification); + if (media && m_imagePath.isEmpty() && m_image.isNull()) + { + m_update |= kNoArtwork; + } + if (!notification.GetMetaData().isEmpty()) { UpdateMetaData(notification.GetMetaData()); @@ -215,6 +224,16 @@ bool MythUINotificationScreen::Create(void) m_progresstextText = dynamic_cast(GetChild("progress_text")); m_progressBar = dynamic_cast(GetChild("progress")); m_errorState = dynamic_cast(GetChild("errorstate")); + m_mediaState = dynamic_cast(GetChild("mediastate")); + + if (m_errorState) + { + m_errorState->DisplayState(m_content & kError ? "error" : "ok"); + } + if (m_mediaState && (m_update & kImage)) + { + m_mediaState->DisplayState(m_content & kNoArtwork ? "noartwork" : "ok"); + } // store original position m_position = GetPosition(); @@ -334,6 +353,10 @@ void MythUINotificationScreen::Init(void) { m_errorState->DisplayState(m_update & kError ? "error" : "ok"); } + if (m_mediaState && (m_update & kImage)) + { + m_mediaState->DisplayState(m_update & kNoArtwork ? "noartwork" : "ok"); + } // No field will be refreshed the next time unless specified otherwise m_update = kNone; diff --git a/mythtv/libs/libmythui/mythuinotificationcenter_private.h b/mythtv/libs/libmythui/mythuinotificationcenter_private.h index 20c71e9afb0..a6cb865284b 100644 --- a/mythtv/libs/libmythui/mythuinotificationcenter_private.h +++ b/mythtv/libs/libmythui/mythuinotificationcenter_private.h @@ -185,6 +185,7 @@ class MythUINotificationScreen : public MythScreenType kMetaData = 1 << 2, kStyle = 1 << 3, kError = 1 << 4, + kNoArtwork = 1 << 5, kAll = ~kNone, }; @@ -220,6 +221,7 @@ public slots: MythUIText *m_progresstextText; MythUIProgressBar *m_progressBar; MythUIStateType *m_errorState; + MythUIStateType *m_mediaState; QDateTime m_creation, m_expiry; int m_index; MythPoint m_position; From 11ab3b56b151877131c403980c371e07ff984909 Mon Sep 17 00:00:00 2001 From: Jean-Yves Avenard Date: Mon, 15 Jul 2013 12:54:10 +1000 Subject: [PATCH 5/5] Update default notification theme to support no artwork condition Add image from Artist: Vargas21 http://www.iconarchive.com/show/aquave-metal-icons-by-vargas21/Music-icon.html --- mythtv/themes/default/noartwork.png | Bin 0 -> 15361 bytes mythtv/themes/default/notification-ui.xml | 15 ++++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 mythtv/themes/default/noartwork.png diff --git a/mythtv/themes/default/noartwork.png b/mythtv/themes/default/noartwork.png new file mode 100644 index 0000000000000000000000000000000000000000..1e41a0353fc978c725f7e5bf3629c25e96fbce55 GIT binary patch literal 15361 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 fFJA-TE57_+Z1p=qGcg0,0,100%,100% + + 0,0,100%,100% + + + 0,0,100%,100% + + + noartwork.png + 18,18,144,144 + + + + 0,0,100%,100% @@ -93,7 +106,7 @@ - + damaged.png 18,18,144,144