aboutsummaryrefslogtreecommitdiff
path: root/src/QXmppRtpChannel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/QXmppRtpChannel.cpp')
-rw-r--r--src/QXmppRtpChannel.cpp1042
1 files changed, 0 insertions, 1042 deletions
diff --git a/src/QXmppRtpChannel.cpp b/src/QXmppRtpChannel.cpp
deleted file mode 100644
index df26bc30..00000000
--- a/src/QXmppRtpChannel.cpp
+++ /dev/null
@@ -1,1042 +0,0 @@
-/*
- * Copyright (C) 2008-2011 The QXmpp developers
- *
- * Author:
- * Jeremy Lainé
- *
- * Source:
- * http://code.google.com/p/qxmpp
- *
- * This file is a part of QXmpp library.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- */
-
-#include <cmath>
-
-#include <QDataStream>
-#include <QMetaType>
-#include <QTimer>
-
-#include "QXmppCodec.h"
-#include "QXmppJingleIq.h"
-#include "QXmppRtpChannel.h"
-
-#ifndef M_PI
-#define M_PI 3.14159265358979323846264338327950288
-#endif
-
-//#define QXMPP_DEBUG_RTP
-//#define QXMPP_DEBUG_RTP_BUFFER
-#define SAMPLE_BYTES 2
-
-const quint8 RTP_VERSION = 0x02;
-
-/// Parses an RTP packet.
-///
-/// \param ba
-
-bool QXmppRtpPacket::decode(const QByteArray &ba)
-{
- if (ba.isEmpty())
- return false;
-
- // fixed header
- quint8 tmp;
- QDataStream stream(ba);
- stream >> tmp;
- version = (tmp >> 6);
- const quint8 cc = (tmp >> 1) & 0xf;
- const int hlen = 12 + 4 * cc;
- if (version != RTP_VERSION || ba.size() < hlen)
- return false;
- stream >> tmp;
- marker = (tmp >> 7);
- type = tmp & 0x7f;
- stream >> sequence;
- stream >> stamp;
- stream >> ssrc;
-
- // contributing source IDs
- csrc.clear();
- quint32 src;
- for (int i = 0; i < cc; ++i) {
- stream >> src;
- csrc << src;
- }
-
- // retrieve payload
- payload = ba.right(ba.size() - hlen);
- return true;
-}
-
-/// Encodes an RTP packet.
-
-QByteArray QXmppRtpPacket::encode() const
-{
- Q_ASSERT(csrc.size() < 16);
-
- // fixed header
- QByteArray ba;
- ba.resize(payload.size() + 12 + 4 * csrc.size());
- QDataStream stream(&ba, QIODevice::WriteOnly);
- stream << quint8(((version & 0x3) << 6) |
- ((csrc.size() & 0xf) << 1));
- stream << quint8((type & 0x7f) | (marker << 7));
- stream << sequence;
- stream << stamp;
- stream << ssrc;
-
- // contributing source ids
- foreach (const quint32 &src, csrc)
- stream << src;
-
- stream.writeRawData(payload.constData(), payload.size());
- return ba;
-}
-
-/// Returns a string representation of the RTP header.
-
-QString QXmppRtpPacket::toString() const
-{
- return QString("RTP packet seq %1 stamp %2 marker %3 type %4 size %5").arg(
- QString::number(sequence),
- QString::number(stamp),
- QString::number(marker),
- QString::number(type),
- QString::number(payload.size()));
-}
-
-/// Creates a new RTP channel.
-
-QXmppRtpChannel::QXmppRtpChannel()
- : m_outgoingPayloadNumbered(false)
-{
-}
-
-/// Returns the local payload types.
-///
-
-QList<QXmppJinglePayloadType> QXmppRtpChannel::localPayloadTypes()
-{
- m_outgoingPayloadNumbered = true;
- return m_outgoingPayloadTypes;
-}
-
-/// Sets the remote payload types.
-///
-/// \param remotePayloadTypes
-
-void QXmppRtpChannel::setRemotePayloadTypes(const QList<QXmppJinglePayloadType> &remotePayloadTypes)
-{
- QList<QXmppJinglePayloadType> commonOutgoingTypes;
- QList<QXmppJinglePayloadType> commonIncomingTypes;
-
- foreach (const QXmppJinglePayloadType &incomingType, remotePayloadTypes) {
- // check we support this payload type
- int outgoingIndex = m_outgoingPayloadTypes.indexOf(incomingType);
- if (outgoingIndex < 0)
- continue;
- QXmppJinglePayloadType outgoingType = m_outgoingPayloadTypes[outgoingIndex];
-
- // be kind and try to adopt the other agent's numbering
- if (!m_outgoingPayloadNumbered && outgoingType.id() > 95) {
- outgoingType.setId(incomingType.id());
- }
- commonIncomingTypes << incomingType;
- commonOutgoingTypes << outgoingType;
- }
- if (commonOutgoingTypes.isEmpty()) {
- qWarning("QXmppRtpChannel could not negociate a common codec");
- return;
- }
- m_incomingPayloadTypes = commonIncomingTypes;
- m_outgoingPayloadTypes = commonOutgoingTypes;
- m_outgoingPayloadNumbered = true;
-
- // call hook
- payloadTypesChanged();
-}
-
-void QXmppRtpChannel::payloadTypesChanged()
-{
-}
-
-enum CodecId {
- G711u = 0,
- GSM = 3,
- G723 = 4,
- G711a = 8,
- G722 = 9,
- L16Stereo = 10,
- L16Mono = 11,
- G728 = 15,
- G729 = 18,
-};
-
-struct ToneInfo
-{
- QXmppRtpAudioChannel::Tone tone;
- quint32 incomingStart;
- quint32 outgoingStart;
- bool finished;
-};
-
-static QPair<int, int> toneFreqs(QXmppRtpAudioChannel::Tone tone)
-{
- switch (tone) {
- case QXmppRtpAudioChannel::Tone_1: return qMakePair(697, 1209);
- case QXmppRtpAudioChannel::Tone_2: return qMakePair(697, 1336);
- case QXmppRtpAudioChannel::Tone_3: return qMakePair(697, 1477);
- case QXmppRtpAudioChannel::Tone_A: return qMakePair(697, 1633);
- case QXmppRtpAudioChannel::Tone_4: return qMakePair(770, 1209);
- case QXmppRtpAudioChannel::Tone_5: return qMakePair(770, 1336);
- case QXmppRtpAudioChannel::Tone_6: return qMakePair(770, 1477);
- case QXmppRtpAudioChannel::Tone_B: return qMakePair(770, 1633);
- case QXmppRtpAudioChannel::Tone_7: return qMakePair(852, 1209);
- case QXmppRtpAudioChannel::Tone_8: return qMakePair(852, 1336);
- case QXmppRtpAudioChannel::Tone_9: return qMakePair(852, 1477);
- case QXmppRtpAudioChannel::Tone_C: return qMakePair(852, 1633);
- case QXmppRtpAudioChannel::Tone_Star: return qMakePair(941, 1209);
- case QXmppRtpAudioChannel::Tone_0: return qMakePair(941, 1336);
- case QXmppRtpAudioChannel::Tone_Pound: return qMakePair(941, 1477);
- case QXmppRtpAudioChannel::Tone_D: return qMakePair(941, 1633);
- }
- return qMakePair(0, 0);
-}
-
-QByteArray renderTone(QXmppRtpAudioChannel::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 QXmppRtpAudioChannelPrivate
-{
-public:
- QXmppRtpAudioChannelPrivate(QXmppRtpAudioChannel *qq);
- QXmppCodec *codecForPayloadType(const QXmppJinglePayloadType &payloadType);
-
- // signals
- bool signalsEmitted;
- qint64 writtenSinceLastEmit;
-
- // RTP
- QHostAddress remoteHost;
- quint16 remotePort;
-
- QByteArray incomingBuffer;
- bool incomingBuffering;
- QMap<int, QXmppCodec*> incomingCodecs;
- int incomingMinimum;
- int incomingMaximum;
- // position of the head of the incoming buffer, in bytes
- qint64 incomingPos;
- quint16 incomingSequence;
-
- QByteArray outgoingBuffer;
- quint16 outgoingChunk;
- QXmppCodec *outgoingCodec;
- bool outgoingMarker;
- bool outgoingPayloadNumbered;
- quint16 outgoingSequence;
- quint32 outgoingStamp;
- QTimer *outgoingTimer;
- QList<ToneInfo> outgoingTones;
- QXmppJinglePayloadType outgoingTonesType;
-
- quint32 outgoingSsrc;
- QXmppJinglePayloadType payloadType;
-
-private:
- QXmppRtpAudioChannel *q;
-};
-
-QXmppRtpAudioChannelPrivate::QXmppRtpAudioChannelPrivate(QXmppRtpAudioChannel *qq)
- : signalsEmitted(false),
- writtenSinceLastEmit(0),
- incomingBuffering(true),
- incomingMinimum(0),
- incomingMaximum(0),
- incomingPos(0),
- incomingSequence(0),
- outgoingCodec(0),
- outgoingMarker(true),
- outgoingPayloadNumbered(false),
- outgoingSequence(1),
- outgoingStamp(0),
- outgoingSsrc(0),
- q(qq)
-{
- qRegisterMetaType<QXmppRtpAudioChannel::Tone>("QXmppRtpAudioChannel::Tone");
- outgoingSsrc = qrand();
-}
-
-/// Returns the audio codec for the given payload type.
-///
-
-QXmppCodec *QXmppRtpAudioChannelPrivate::codecForPayloadType(const QXmppJinglePayloadType &payloadType)
-{
- if (payloadType.id() == G711u)
- return new QXmppG711uCodec(payloadType.clockrate());
- else if (payloadType.id() == G711a)
- return new QXmppG711aCodec(payloadType.clockrate());
-#ifdef QXMPP_USE_SPEEX
- else if (payloadType.name().toLower() == "speex")
- return new QXmppSpeexCodec(payloadType.clockrate());
-#endif
- return 0;
-}
-
-/// Creates a new RTP audio channel.
-///
-/// \param parent
-
-QXmppRtpAudioChannel::QXmppRtpAudioChannel(QObject *parent)
- : QIODevice(parent)
-{
- d = new QXmppRtpAudioChannelPrivate(this);
- QXmppLoggable *logParent = qobject_cast<QXmppLoggable*>(parent);
- if (logParent) {
- connect(this, SIGNAL(logMessage(QXmppLogger::MessageType,QString)),
- logParent, SIGNAL(logMessage(QXmppLogger::MessageType,QString)));
- }
- d->outgoingTimer = new QTimer(this);
- connect(d->outgoingTimer, SIGNAL(timeout()), this, SLOT(writeDatagram()));
-
- // set supported codecs
- QXmppJinglePayloadType payload;
-
-#ifdef QXMPP_USE_SPEEX
- payload.setId(96);
- payload.setChannels(1);
- payload.setName("speex");
- payload.setClockrate(8000);
- m_outgoingPayloadTypes << payload;
-#endif
-
- payload.setId(G711u);
- payload.setChannels(1);
- payload.setName("PCMU");
- payload.setClockrate(8000);
- m_outgoingPayloadTypes << payload;
-
- payload.setId(G711a);
- payload.setChannels(1);
- payload.setName("PCMA");
- payload.setClockrate(8000);
- m_outgoingPayloadTypes << payload;
-
- QMap<QString, QString> parameters;
- parameters.insert("events", "0-15");
- payload.setId(101);
- payload.setChannels(1);
- payload.setName("telephone-event");
- payload.setClockrate(8000);
- payload.setParameters(parameters);
- m_outgoingPayloadTypes << payload;
-}
-
-/// Destroys an RTP audio channel.
-///
-
-QXmppRtpAudioChannel::~QXmppRtpAudioChannel()
-{
- foreach (QXmppCodec *codec, d->incomingCodecs)
- delete codec;
- if (d->outgoingCodec)
- delete d->outgoingCodec;
- delete d;
-}
-
-/// Returns the number of bytes that are available for reading.
-///
-
-qint64 QXmppRtpAudioChannel::bytesAvailable() const
-{
- return d->incomingBuffer.size();
-}
-
-/// Closes the RTP channel.
-///
-
-void QXmppRtpAudioChannel::close()
-{
- d->outgoingTimer->stop();
- QIODevice::close();
-}
-
-/// Processes an incoming RTP packet.
-///
-/// \param ba
-
-void QXmppRtpAudioChannel::datagramReceived(const QByteArray &ba)
-{
- QXmppRtpPacket packet;
- if (!packet.decode(ba))
- return;
-
-#ifdef QXMPP_DEBUG_RTP
- logReceived(packet.toString());
-#endif
-
- // check sequence number
-#if 0
- if (d->incomingSequence && packet.sequence != d->incomingSequence + 1)
- warning(QString("RTP packet seq %1 is out of order, previous was %2")
- .arg(QString::number(packet.sequence))
- .arg(QString::number(d->incomingSequence)));
-#endif
- d->incomingSequence = packet.sequence;
-
- // get or create codec
- QXmppCodec *codec = 0;
- if (!d->incomingCodecs.contains(packet.type)) {
- foreach (const QXmppJinglePayloadType &payload, m_incomingPayloadTypes) {
- if (packet.type == payload.id()) {
- codec = d->codecForPayloadType(payload);
- break;
- }
- }
- if (codec)
- d->incomingCodecs.insert(packet.type, codec);
- else
- warning(QString("Could not find codec for RTP type %1").arg(QString::number(packet.type)));
- } else {
- codec = d->incomingCodecs.value(packet.type);
- }
- if (!codec)
- return;
-
- // determine packet's position in the buffer (in bytes)
- qint64 packetOffset = 0;
- if (!d->incomingBuffer.isEmpty()) {
- packetOffset = packet.stamp * SAMPLE_BYTES - d->incomingPos;
- if (packetOffset < 0) {
-#ifdef QXMPP_DEBUG_RTP_BUFFER
- warning(QString("RTP packet stamp %1 is too old, buffer start is %2")
- .arg(QString::number(packet.stamp))
- .arg(QString::number(d->incomingPos)));
-#endif
- return;
- }
- } else {
- d->incomingPos = packet.stamp * SAMPLE_BYTES + (d->incomingPos % SAMPLE_BYTES);
- }
-
- // allocate space for new packet
- // FIXME: this is wrong, we want the decoded data size!
- qint64 packetLength = packet.payload.size();
- if (packetOffset + packetLength > d->incomingBuffer.size())
- d->incomingBuffer += QByteArray(packetOffset + packetLength - d->incomingBuffer.size(), 0);
- QDataStream input(packet.payload);
- QDataStream output(&d->incomingBuffer, QIODevice::WriteOnly);
- output.device()->seek(packetOffset);
- output.setByteOrder(QDataStream::LittleEndian);
- codec->decode(input, output);
-
- // check whether we are running late
- if (d->incomingBuffer.size() > d->incomingMaximum)
- {
- qint64 droppedSize = d->incomingBuffer.size() - d->incomingMinimum;
- const int remainder = droppedSize % SAMPLE_BYTES;
- if (remainder)
- droppedSize -= remainder;
-#ifdef QXMPP_DEBUG_RTP_BUFFER
- warning(QString("Incoming RTP buffer is too full, dropping %1 bytes")
- .arg(QString::number(droppedSize)));
-#endif
- d->incomingBuffer.remove(0, droppedSize);
- d->incomingPos += droppedSize;
- }
- // check whether we have filled the initial buffer
- if (d->incomingBuffer.size() >= d->incomingMinimum)
- d->incomingBuffering = false;
- if (!d->incomingBuffering)
- emit readyRead();
-}
-
-void QXmppRtpAudioChannel::emitSignals()
-{
- emit bytesWritten(d->writtenSinceLastEmit);
- d->writtenSinceLastEmit = 0;
- d->signalsEmitted = false;
-}
-
-/// Returns true, as the RTP channel is a sequential device.
-///
-
-bool QXmppRtpAudioChannel::isSequential() const
-{
- return true;
-}
-
-QIODevice::OpenMode QXmppRtpAudioChannel::openMode() const
-{
- return QIODevice::openMode();
-}
-
-qint64 QXmppRtpAudioChannel::readData(char * data, qint64 maxSize)
-{
- // if we are filling the buffer, return empty samples
- if (d->incomingBuffering)
- {
- // FIXME: if we are asked for a non-integer number of samples,
- // we will return junk on next read as we don't increment d->incomingPos
- memset(data, 0, maxSize);
- return maxSize;
- }
-
- qint64 readSize = qMin(maxSize, qint64(d->incomingBuffer.size()));
- memcpy(data, d->incomingBuffer.constData(), readSize);
- d->incomingBuffer.remove(0, readSize);
- if (readSize < maxSize)
- {
-#ifdef QXMPP_DEBUG_RTP
- debug(QString("QXmppRtpAudioChannel::readData missing %1 bytes").arg(QString::number(maxSize - readSize)));
-#endif
- memset(data + readSize, 0, maxSize - readSize);
- }
-
- // add local DTMF echo
- if (!d->outgoingTones.isEmpty()) {
- const int headOffset = d->incomingPos % SAMPLE_BYTES;
- const int samples = (headOffset + maxSize + SAMPLE_BYTES - 1) / SAMPLE_BYTES;
- const QByteArray chunk = renderTone(
- d->outgoingTones[0].tone,
- d->payloadType.clockrate(),
- d->incomingPos / SAMPLE_BYTES - d->outgoingTones[0].incomingStart,
- samples);
- memcpy(data, chunk.constData() + headOffset, maxSize);
- }
-
- d->incomingPos += maxSize;
- return maxSize;
-}
-
-/// Returns the RTP channel's payload type.
-///
-/// You can use this to determine the QAudioFormat to use with your
-/// QAudioInput/QAudioOutput.
-
-QXmppJinglePayloadType QXmppRtpAudioChannel::payloadType() const
-{
- return d->payloadType;
-}
-
-void QXmppRtpAudioChannel::payloadTypesChanged()
-{
- // delete incoming codecs
- foreach (QXmppCodec *codec, d->incomingCodecs)
- delete codec;
- d->incomingCodecs.clear();
-
- // delete outgoing codec
- if (d->outgoingCodec) {
- delete d->outgoingCodec;
- d->outgoingCodec = 0;
- }
-
- // create outgoing codec
- foreach (const QXmppJinglePayloadType &outgoingType, m_outgoingPayloadTypes) {
- // check for telephony events
- if (outgoingType.name() == "telephone-event") {
- d->outgoingTonesType = outgoingType;
- }
- else if (!d->outgoingCodec) {
- QXmppCodec *codec = d->codecForPayloadType(outgoingType);
- if (codec) {
- d->payloadType = outgoingType;
- d->outgoingCodec = codec;
- }
- }
- }
-
- // size in bytes of an decoded packet
- d->outgoingChunk = SAMPLE_BYTES * d->payloadType.ptime() * d->payloadType.clockrate() / 1000;
- d->outgoingTimer->setInterval(d->payloadType.ptime());
-
- d->incomingMinimum = d->outgoingChunk * 5;
- d->incomingMaximum = d->outgoingChunk * 15;
-
- open(QIODevice::ReadWrite | QIODevice::Unbuffered);
-}
-
-/// Returns the position in the received audio data.
-
-qint64 QXmppRtpAudioChannel::pos() const
-{
- return d->incomingPos;
-}
-
-/// Seeks in the received audio data.
-///
-/// Seeking backwards will result in empty samples being added at the start
-/// of the buffer.
-///
-/// \param pos
-
-bool QXmppRtpAudioChannel::seek(qint64 pos)
-{
- qint64 delta = pos - d->incomingPos;
- if (delta < 0)
- d->incomingBuffer.prepend(QByteArray(-delta, 0));
- else
- d->incomingBuffer.remove(0, delta);
- d->incomingPos = pos;
- return true;
-}
-
-/// Starts sending the specified DTMF tone.
-///
-/// \param tone
-
-void QXmppRtpAudioChannel::startTone(QXmppRtpAudioChannel::Tone tone)
-{
- ToneInfo info;
- info.tone = tone;
- info.incomingStart = d->incomingPos / SAMPLE_BYTES;
- info.outgoingStart = d->outgoingStamp;
- info.finished = false;
- d->outgoingTones << info;
-}
-
-/// Stops sending the specified DTMF tone.
-///
-/// \param tone
-
-void QXmppRtpAudioChannel::stopTone(QXmppRtpAudioChannel::Tone tone)
-{
- for (int i = 0; i < d->outgoingTones.size(); ++i) {
- if (d->outgoingTones[i].tone == tone) {
- d->outgoingTones[i].finished = true;
- break;
- }
- }
-}
-
-qint64 QXmppRtpAudioChannel::writeData(const char * data, qint64 maxSize)
-{
- if (!d->outgoingCodec) {
- warning("QXmppRtpAudioChannel::writeData before codec was set");
- return -1;
- }
-
- d->outgoingBuffer += QByteArray::fromRawData(data, maxSize);
-
- // start sending audio chunks
- if (!d->outgoingTimer->isActive())
- d->outgoingTimer->start();
-
- return maxSize;
-}
-
-void QXmppRtpAudioChannel::writeDatagram()
-{
- // read audio chunk
- QByteArray chunk;
- if (d->outgoingBuffer.size() < d->outgoingChunk) {
-#ifdef QXMPP_DEBUG_RTP_BUFFER
- warning("Outgoing RTP buffer is starved");
-#endif
- chunk = QByteArray(d->outgoingChunk, 0);
- } else {
- chunk = d->outgoingBuffer.left(d->outgoingChunk);
- d->outgoingBuffer.remove(0, d->outgoingChunk);
- }
-
- bool sendAudio = true;
- if (!d->outgoingTones.isEmpty()) {
- const quint32 packetTicks = (d->payloadType.clockrate() * d->payloadType.ptime()) / 1000;
- const ToneInfo info = d->outgoingTones[0];
-
- if (d->outgoingTonesType.id()) {
- // send RFC 2833 DTMF
- QXmppRtpPacket packet;
- packet.version = RTP_VERSION;
- packet.marker = (info.outgoingStart == d->outgoingStamp);
- packet.type = d->outgoingTonesType.id();
- packet.sequence = d->outgoingSequence;
- packet.stamp = info.outgoingStart;
- packet.ssrc = d->outgoingSsrc;
-
- QDataStream output(&packet.payload, QIODevice::WriteOnly);
- output << quint8(info.tone);
- output << quint8(info.finished ? 0x80 : 0x00);
- output << quint16(d->outgoingStamp + packetTicks - info.outgoingStart);
-#ifdef QXMPP_DEBUG_RTP
- logSent(packet.toString());
-#endif
- emit sendDatagram(packet.encode());
- d->outgoingSequence++;
- d->outgoingStamp += packetTicks;
-
- sendAudio = false;
- } else {
- // generate in-band DTMF
- chunk = renderTone(info.tone, d->payloadType.clockrate(), d->outgoingStamp - info.outgoingStart, packetTicks);
- }
-
- // if the tone is finished, remove it
- if (info.finished)
- d->outgoingTones.removeFirst();
- }
-
- if (sendAudio) {
- // send audio data
- QXmppRtpPacket packet;
- packet.version = RTP_VERSION;
- if (d->outgoingMarker)
- {
- packet.marker = true;
- d->outgoingMarker = false;
- } else {
- packet.marker = false;
- }
- packet.type = d->payloadType.id();
- packet.sequence = d->outgoingSequence;
- packet.stamp = d->outgoingStamp;
- packet.ssrc = d->outgoingSsrc;
-
- // encode audio chunk
- QDataStream input(chunk);
- input.setByteOrder(QDataStream::LittleEndian);
- QDataStream output(&packet.payload, QIODevice::WriteOnly);
- const qint64 packetTicks = d->outgoingCodec->encode(input, output);
-
-#ifdef QXMPP_DEBUG_RTP
- logSent(packet.toString());
-#endif
- emit sendDatagram(packet.encode());
- d->outgoingSequence++;
- d->outgoingStamp += packetTicks;
- }
-
- // queue signals
- d->writtenSinceLastEmit += chunk.size();
- if (!d->signalsEmitted && !signalsBlocked()) {
- d->signalsEmitted = true;
- QMetaObject::invokeMethod(this, "emitSignals", Qt::QueuedConnection);
- }
-}
-
-/** Constructs a null video frame.
- */
-QXmppVideoFrame::QXmppVideoFrame()
- : m_bytesPerLine(0),
- m_height(0),
- m_mappedBytes(0),
- m_pixelFormat(Format_Invalid),
- m_width(0)
-{
-}
-
-/** Constructs a video frame of the given pixel format and size in pixels.
- *
- * @param bytes
- * @param size
- * @param bytesPerLine
- * @param format
- */
-QXmppVideoFrame::QXmppVideoFrame(int bytes, const QSize &size, int bytesPerLine, PixelFormat format)
- : m_bytesPerLine(bytesPerLine),
- m_height(size.height()),
- m_mappedBytes(bytes),
- m_pixelFormat(format),
- m_width(size.width())
-{
- m_data.resize(bytes);
-}
-
-uchar *QXmppVideoFrame::bits()
-{
- return (uchar*)m_data.data();
-}
-
-const uchar *QXmppVideoFrame::bits() const
-{
- return (const uchar*)m_data.constData();
-}
-
-/** Returns the number of bytes in a scan line.
- */
-int QXmppVideoFrame::bytesPerLine() const
-{
- return m_bytesPerLine;
-}
-
-/** Returns the height of a video frame.
- */
-int QXmppVideoFrame::height() const
-{
- return m_height;
-}
-
-/** Returns true if the frame is valid.
- */
-bool QXmppVideoFrame::isValid() const
-{
- return m_pixelFormat != Format_Invalid &&
- m_height > 0 && m_width > 0 &&
- m_mappedBytes > 0;
-}
-
-/** Returns the number of bytes occupied by the mapped frame data.
- */
-int QXmppVideoFrame::mappedBytes() const
-{
- return m_mappedBytes;
-}
-
-/** Returns the color format of a video frame.
- */
-QXmppVideoFrame::PixelFormat QXmppVideoFrame::pixelFormat() const
-{
- return m_pixelFormat;
-}
-
-/** Returns the size of a video frame.
- */
-QSize QXmppVideoFrame::size() const
-{
- return QSize(m_width, m_height);
-}
-
-/** Returns the width of a video frame.
- */
-int QXmppVideoFrame::width() const
-{
- return m_width;
-}
-
-class QXmppRtpVideoChannelPrivate
-{
-public:
- QXmppRtpVideoChannelPrivate();
- QMap<int, QXmppVideoDecoder*> decoders;
- QXmppVideoEncoder *encoder;
- QList<QXmppVideoFrame> frames;
-
- // local
- QXmppVideoFormat outgoingFormat;
- quint8 outgoingId;
- quint16 outgoingSequence;
- quint32 outgoingStamp;
- quint32 outgoingSsrc;
-};
-
-QXmppRtpVideoChannelPrivate::QXmppRtpVideoChannelPrivate()
- : encoder(0),
- outgoingId(0),
- outgoingSequence(1),
- outgoingStamp(0),
- outgoingSsrc(0)
-{
- outgoingSsrc = qrand();
-}
-
-QXmppRtpVideoChannel::QXmppRtpVideoChannel(QObject *parent)
- : QXmppLoggable(parent)
-{
- d = new QXmppRtpVideoChannelPrivate;
- d->outgoingFormat.setFrameRate(15.0);
- d->outgoingFormat.setFrameSize(QSize(320, 240));
- d->outgoingFormat.setPixelFormat(QXmppVideoFrame::Format_YUYV);
-
- // set supported codecs
- QXmppVideoEncoder *encoder;
- QXmppJinglePayloadType payload;
- Q_UNUSED(encoder);
- Q_UNUSED(payload);
-
-#ifdef QXMPP_USE_VPX
- encoder = new QXmppVpxEncoder;
- encoder->setFormat(d->outgoingFormat);
- payload.setId(96);
- payload.setName("vp8");
- payload.setClockrate(90000);
- payload.setParameters(encoder->parameters());
- m_outgoingPayloadTypes << payload;
- delete encoder;
-#endif
-
-#ifdef QXMPP_USE_THEORA
- encoder = new QXmppTheoraEncoder;
- encoder->setFormat(d->outgoingFormat);
- payload.setId(97);
- payload.setName("theora");
- payload.setClockrate(90000);
- payload.setParameters(encoder->parameters());
- m_outgoingPayloadTypes << payload;
- delete encoder;
-#endif
-}
-
-QXmppRtpVideoChannel::~QXmppRtpVideoChannel()
-{
- foreach (QXmppVideoDecoder *decoder, d->decoders)
- delete decoder;
- if (d->encoder)
- delete d->encoder;
- delete d;
-}
-
-/// Closes the RTP channel.
-///
-
-void QXmppRtpVideoChannel::close()
-{
-}
-
-/// Processes an incoming RTP video packet.
-///
-/// \param ba
-
-void QXmppRtpVideoChannel::datagramReceived(const QByteArray &ba)
-{
- QXmppRtpPacket packet;
- if (!packet.decode(ba))
- return;
-
-#ifdef QXMPP_DEBUG_RTP
- logReceived(packet.toString());
-#endif
-
- // get codec
- QXmppVideoDecoder *decoder = d->decoders.value(packet.type);
- if (!decoder)
- return;
- d->frames << decoder->handlePacket(packet);
-}
-
-QXmppVideoFormat QXmppRtpVideoChannel::decoderFormat() const
-{
- if (d->decoders.isEmpty())
- return QXmppVideoFormat();
- const int key = d->decoders.keys().first();
- return d->decoders.value(key)->format();
-}
-
-QXmppVideoFormat QXmppRtpVideoChannel::encoderFormat() const
-{
- return d->outgoingFormat;
-}
-
-void QXmppRtpVideoChannel::setEncoderFormat(const QXmppVideoFormat &format)
-{
- if (d->encoder && !d->encoder->setFormat(format))
- return;
- d->outgoingFormat = format;
-}
-
-QIODevice::OpenMode QXmppRtpVideoChannel::openMode() const
-{
- QIODevice::OpenMode mode = QIODevice::NotOpen;
- if (!d->decoders.isEmpty())
- mode |= QIODevice::ReadOnly;
- if (d->encoder)
- mode |= QIODevice::WriteOnly;
- return mode;
-}
-
-void QXmppRtpVideoChannel::payloadTypesChanged()
-{
- // refresh decoders
- foreach (QXmppVideoDecoder *decoder, d->decoders)
- delete decoder;
- d->decoders.clear();
- foreach (const QXmppJinglePayloadType &payload, m_incomingPayloadTypes) {
- QXmppVideoDecoder *decoder = 0;
- if (false)
- {}
-#ifdef QXMPP_USE_THEORA
- else if (payload.name().toLower() == "theora")
- decoder = new QXmppTheoraDecoder;
-#endif
-#ifdef QXMPP_USE_VPX
- else if (payload.name().toLower() == "vp8")
- decoder = new QXmppVpxDecoder;
-#endif
- if (decoder) {
- decoder->setParameters(payload.parameters());
- d->decoders.insert(payload.id(), decoder);
- }
- }
-
- // refresh encoder
- if (d->encoder) {
- delete d->encoder;
- d->encoder = 0;
- }
- foreach (const QXmppJinglePayloadType &payload, m_outgoingPayloadTypes) {
- QXmppVideoEncoder *encoder = 0;
- if (false)
- {}
-#ifdef QXMPP_USE_THEORA
- else if (payload.name().toLower() == "theora")
- encoder = new QXmppTheoraEncoder;
-#endif
-#ifdef QXMPP_USE_VPX
- else if (payload.name().toLower() == "vp8") {
- encoder = new QXmppVpxEncoder;
- }
-#endif
- if (encoder) {
- encoder->setFormat(d->outgoingFormat);
- d->encoder = encoder;
- d->outgoingId = payload.id();
- break;
- }
- }
-}
-
-QList<QXmppVideoFrame> QXmppRtpVideoChannel::readFrames()
-{
- const QList<QXmppVideoFrame> frames = d->frames;
- d->frames.clear();
- return frames;
-}
-
-void QXmppRtpVideoChannel::writeFrame(const QXmppVideoFrame &frame)
-{
- if (!d->encoder) {
- warning("QXmppRtpVideoChannel::writeFrame before codec was set");
- return;
- }
-
- QXmppRtpPacket packet;
- packet.version = RTP_VERSION;
- packet.marker = false;
- packet.type = d->outgoingId;
- packet.ssrc = d->outgoingSsrc;
- foreach (const QByteArray &payload, d->encoder->handleFrame(frame)) {
- packet.sequence = d->outgoingSequence++;
- packet.stamp = d->outgoingStamp;
- packet.payload = payload;
-#ifdef QXMPP_DEBUG_RTP
- logSent(packet.toString());
-#endif
- emit sendDatagram(packet.encode());
- }
- d->outgoingStamp += 1;
-}
-