aboutsummaryrefslogtreecommitdiff
path: root/src/QXmppRtpChannel.cpp
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2010-12-09 16:11:26 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2010-12-09 16:11:26 +0000
commitd038abbdfb50ac3399e0bf4079cda0d1e1f72c91 (patch)
tree758b3cd51eeb2e13964e9b54a3505fd2e2f515d4 /src/QXmppRtpChannel.cpp
parentc2ee0b903b9a937b19481caf090012eee719853a (diff)
downloadqxmpp-d038abbdfb50ac3399e0bf4079cda0d1e1f72c91.tar.gz
* fix API documentation for QXmppRtpChannel
* generate local echo for DTMF tones
Diffstat (limited to 'src/QXmppRtpChannel.cpp')
-rw-r--r--src/QXmppRtpChannel.cpp162
1 files changed, 101 insertions, 61 deletions
diff --git a/src/QXmppRtpChannel.cpp b/src/QXmppRtpChannel.cpp
index f37b09e1..b1b02328 100644
--- a/src/QXmppRtpChannel.cpp
+++ b/src/QXmppRtpChannel.cpp
@@ -36,6 +36,18 @@
const quint8 RTP_VERSION = 0x02;
+enum CodecId {
+ G711u = 0,
+ GSM = 3,
+ G723 = 4,
+ G711a = 8,
+ G722 = 9,
+ L16Stereo = 10,
+ L16Mono = 11,
+ G728 = 15,
+ G729 = 18,
+};
+
struct ToneInfo
{
QXmppRtpChannel::Tone tone;
@@ -67,6 +79,22 @@ static QPair<int, int> toneFreqs(QXmppRtpChannel::Tone tone)
return qMakePair(0, 0);
}
+QByteArray renderTone(QXmppRtpChannel::Tone tone, int clockrate, quint32 clockTick, qint64 samples)
+{
+ QPair<int,int> tf = toneFreqs(tone);
+ const float clockMult = 2.0 * M_PI / float(clockrate);
+ QByteArray chunk;
+ chunk.reserve(samples * SAMPLE_BYTES);
+ QDataStream output(&chunk, QIODevice::WriteOnly);
+ output.setByteOrder(QDataStream::LittleEndian);
+ for (quint32 i = 0; i < samples; ++i) {
+ quint16 val = 16383.0 * (sin(clockMult * clockTick * tf.first) + sin(clockMult * clockTick * tf.second));
+ output << val;
+ clockTick++;
+ }
+ return chunk;
+}
+
class QXmppRtpChannelPrivate
{
public:
@@ -131,9 +159,9 @@ QXmppRtpChannelPrivate::QXmppRtpChannelPrivate()
QXmppCodec *QXmppRtpChannelPrivate::codecForPayloadType(const QXmppJinglePayloadType &payloadType)
{
- if (payloadType.id() == QXmppRtpChannel::G711u)
+ if (payloadType.id() == G711u)
return new QXmppG711uCodec(payloadType.clockrate());
- else if (payloadType.id() == QXmppRtpChannel::G711a)
+ else if (payloadType.id() == G711a)
return new QXmppG711aCodec(payloadType.clockrate());
#ifdef QXMPP_USE_SPEEX
else if (payloadType.name().toLower() == "speex")
@@ -158,13 +186,13 @@ QList<QXmppJinglePayloadType> QXmppRtpChannelPrivate::supportedPayloadTypes() co
payloads << payload;
#endif
- payload.setId(QXmppRtpChannel::G711u);
+ payload.setId(G711u);
payload.setChannels(1);
payload.setName("PCMU");
payload.setClockrate(8000);
payloads << payload;
- payload.setId(QXmppRtpChannel::G711a);
+ payload.setId(G711a);
payload.setChannels(1);
payload.setName("PCMA");
payload.setClockrate(8000);
@@ -340,6 +368,17 @@ qint64 QXmppRtpChannel::readData(char * data, qint64 maxSize)
#endif
memset(data + readSize, 0, maxSize - readSize);
}
+
+ // add local DTMF echo
+ if (!d->outgoingTones.isEmpty()) {
+ const QByteArray chunk = renderTone(
+ d->outgoingTones[0].tone,
+ d->payloadType.clockrate(),
+ d->incomingStamp - d->outgoingTones[0].incomingStart,
+ maxSize / SAMPLE_BYTES);
+ memcpy(data, chunk.constData(), chunk.size());
+ }
+
d->incomingStamp += readSize / SAMPLE_BYTES;
return maxSize;
}
@@ -479,37 +518,72 @@ void QXmppRtpChannel::writeDatagram()
QByteArray chunk = d->outgoingBuffer.left(d->outgoingChunk);
d->outgoingBuffer.remove(0, chunk.size());
+ bool sendAudio = true;
if (!d->outgoingTones.isEmpty()) {
- const quint32 packetTicks = (d->outgoingTonesType.clockrate() * d->outgoingTonesType.ptime()) / 1000;
-
- // generate in-band DTMF
- QPair<int,int> tf = toneFreqs(d->outgoingTones[0].tone);
- const float clockMult = 2.0 * M_PI / float(d->outgoingTonesType.clockrate());
- chunk.clear();
- QDataStream output(&chunk, QIODevice::WriteOnly);
- output.setByteOrder(QDataStream::LittleEndian);
- quint32 clockTick = d->outgoingStamp;
- for (quint32 i = 0; i < packetTicks; ++i) {
- quint16 val = 16383.0 * cos(clockMult * clockTick * tf.first)
- + 16383.0 * cos(clockMult * clockTick * tf.second);
- output << val;
- clockTick++;
+ const quint32 packetTicks = (d->payloadType.clockrate() * d->payloadType.ptime()) / 1000;
+ const ToneInfo info = d->outgoingTones[0];
+
+ if (d->outgoingTonesType.id()) {
+ // send RFC 2833 DTMF
+ QByteArray header;
+ QDataStream stream(&header, QIODevice::WriteOnly);
+ quint8 marker_type = d->outgoingTonesType.id();
+ if (info.outgoingStart == d->outgoingStamp)
+ marker_type |= 0x80;
+
+ stream << quint8(RTP_VERSION << 6);
+ stream << marker_type;
+ stream << d->outgoingSequence;
+ stream << info.outgoingStart;
+ stream << d->ssrc;
+
+ stream << quint8(info.tone);
+ stream << quint8(info.finished ? 0x80 : 0x00);
+ stream << quint16(d->outgoingStamp + packetTicks - info.outgoingStart);
+#ifdef QXMPP_DEBUG_RTP
+ logSent(QString("RTP packet seq %1 stamp %2 marker %3 type %4 size %5").arg(
+ QString::number(d->outgoingSequence),
+ QString::number(d->outgoingStamp),
+ QString::number(marker_type & 0x80 != 0),
+ QString::number(marker_type & 0x7f),
+ QString::number(header.size() - 12)));
+#endif
+ emit sendDatagram(header);
+ d->outgoingSequence++;
+ d->outgoingStamp += packetTicks;
+
+ sendAudio = false;
+ } else {
+ // generate in-band DTMF
+ chunk = renderTone(info.tone, d->payloadType.clockrate(), d->outgoingStamp - info.outgoingStart, packetTicks);
}
- // send RFC 2833 DTMF
+ // if the tone is finished, remove it
+ if (info.finished)
+ d->outgoingTones.removeFirst();
+ }
+
+ if (sendAudio) {
+ // send audio data
QByteArray header;
QDataStream stream(&header, QIODevice::WriteOnly);
- quint8 marker_type = d->outgoingTonesType.id();
-
+ quint8 marker_type = d->payloadType.id();
+ if (d->outgoingMarker)
+ {
+ marker_type |= 0x80;
+ d->outgoingMarker= false;
+ }
stream << quint8(RTP_VERSION << 6);
stream << marker_type;
stream << d->outgoingSequence;
- stream << d->outgoingTones[0].outgoingStart;
+ stream << d->outgoingStamp;
stream << d->ssrc;
- stream << quint8(d->outgoingTones[0].tone);
- stream << quint8(d->outgoingTones[0].finished ? 0x80 : 0x00);
- stream << quint16(d->outgoingStamp + packetTicks - d->outgoingTones[0].outgoingStart);
+ // encode audio chunk
+ QDataStream input(chunk);
+ input.setByteOrder(QDataStream::LittleEndian);
+ const qint64 packetTicks = d->outgoingCodec->encode(input, stream);
+
#ifdef QXMPP_DEBUG_RTP
logSent(QString("RTP packet seq %1 stamp %2 marker %3 type %4 size %5").arg(
QString::number(d->outgoingSequence),
@@ -520,44 +594,10 @@ void QXmppRtpChannel::writeDatagram()
#endif
emit sendDatagram(header);
d->outgoingSequence++;
-
- // if the tone is finished, remove it
- if (d->outgoingTones[0].finished)
- d->outgoingTones.removeFirst();
+ d->outgoingStamp += packetTicks;
}
- // send audio data
- QByteArray header;
- QDataStream stream(&header, QIODevice::WriteOnly);
- quint8 marker_type = d->payloadType.id();
- if (d->outgoingMarker)
- {
- marker_type |= 0x80;
- d->outgoingMarker= false;
- }
- stream << quint8(RTP_VERSION << 6);
- stream << marker_type;
- stream << d->outgoingSequence;
- stream << d->outgoingStamp;
- stream << d->ssrc;
-
- // encode audio chunk
- QDataStream input(chunk);
- input.setByteOrder(QDataStream::LittleEndian);
- const qint64 packetTicks = d->outgoingCodec->encode(input, stream);
-
-#ifdef QXMPP_DEBUG_RTP
- logSent(QString("RTP packet seq %1 stamp %2 marker %3 type %4 size %5").arg(
- QString::number(d->outgoingSequence),
- QString::number(d->outgoingStamp),
- QString::number(marker_type & 0x80 != 0),
- QString::number(marker_type & 0x7f),
- QString::number(header.size() - 12)));
-#endif
- emit sendDatagram(header);
- d->outgoingSequence++;
- d->outgoingStamp += packetTicks;
-
+ // queue signals
d->writtenSinceLastEmit += chunk.size();
if (!d->signalsEmitted && !signalsBlocked()) {
d->signalsEmitted = true;