diff options
| author | Jeremy Lainé <jeremy.laine@m4x.org> | 2011-07-26 08:52:00 +0000 |
|---|---|---|
| committer | Jeremy Lainé <jeremy.laine@m4x.org> | 2011-07-26 08:52:00 +0000 |
| commit | 3865d6087639e60a8bef07bf7dd88971744bacc8 (patch) | |
| tree | c3b27bb9996e75106eb85c5a4405ad18e3056080 /src | |
| parent | 413651e1a8f7886a2c053141be33fe9c1361ede9 (diff) | |
| download | qxmpp-3865d6087639e60a8bef07bf7dd88971744bacc8.tar.gz | |
add support for vp8 video codec
Diffstat (limited to 'src')
| -rw-r--r-- | src/QXmppCallManager.cpp | 132 | ||||
| -rw-r--r-- | src/QXmppCallManager.h | 9 | ||||
| -rw-r--r-- | src/QXmppCodec.cpp | 346 | ||||
| -rw-r--r-- | src/QXmppCodec.h | 38 | ||||
| -rw-r--r-- | src/QXmppRtpChannel.cpp | 40 | ||||
| -rw-r--r-- | src/QXmppRtpChannel.h | 1 | ||||
| -rw-r--r-- | src/src.pro | 4 |
7 files changed, 494 insertions, 76 deletions
diff --git a/src/QXmppCallManager.cpp b/src/QXmppCallManager.cpp index 30a7d209..7742711a 100644 --- a/src/QXmppCallManager.cpp +++ b/src/QXmppCallManager.cpp @@ -787,7 +787,7 @@ bool QXmppCallManager::handleStanza(const QDomElement &element) { QXmppJingleIq jingleIq; jingleIq.parse(element); - jingleIqReceived(jingleIq); + _q_jingleIqReceived(jingleIq); return true; } } @@ -797,12 +797,22 @@ bool QXmppCallManager::handleStanza(const QDomElement &element) void QXmppCallManager::setClient(QXmppClient *client) { + bool check; + Q_UNUSED(check); + QXmppClientExtension::setClient(client); - bool check = connect(client, SIGNAL(iqReceived(QXmppIq)), - this, SLOT(iqReceived(QXmppIq))); + check = connect(client, SIGNAL(disconnected()), + this, SLOT(_q_disconnected())); + Q_ASSERT(check); + + check = connect(client, SIGNAL(iqReceived(QXmppIq)), + this, SLOT(_q_iqReceived(QXmppIq))); + Q_ASSERT(check); + + check = connect(client, SIGNAL(presenceReceived(QXmppPresence)), + this, SLOT(_q_presenceReceived(QXmppPresence))); Q_ASSERT(check); - Q_UNUSED(check); } /// Initiates a new outgoing call to the specified recipient. @@ -811,6 +821,11 @@ void QXmppCallManager::setClient(QXmppClient *client) QXmppCall *QXmppCallManager::call(const QString &jid) { + if (jid.isEmpty()) { + warning("Refusing to call an empty jid"); + return 0; + } + if (jid == client()->configuration().jid()) { warning("Refusing to call self"); return 0; @@ -822,7 +837,7 @@ QXmppCall *QXmppCallManager::call(const QString &jid) // register call d->calls << call; connect(call, SIGNAL(destroyed(QObject*)), - this, SLOT(callDestroyed(QObject*))); + this, SLOT(_q_callDestroyed(QObject*))); emit callStarted(call); call->d->sendInvite(); @@ -830,15 +845,66 @@ QXmppCall *QXmppCallManager::call(const QString &jid) return call; } -void QXmppCallManager::callDestroyed(QObject *object) +/// Sets the STUN server to use to determine server-reflexive addresses +/// and ports. +/// +/// \param host The address of the STUN server. +/// \param port The port of the STUN server. + +void QXmppCallManager::setStunServer(const QHostAddress &host, quint16 port) +{ + d->stunHost = host; + d->stunPort = port; +} + +/// Sets the TURN server to use to relay packets in double-NAT configurations. +/// +/// \param host The address of the TURN server. +/// \param port The port of the TURN server. + +void QXmppCallManager::setTurnServer(const QHostAddress &host, quint16 port) +{ + d->turnHost = host; + d->turnPort = port; +} + +/// Sets the \a user used for authentication with the TURN server. +/// +/// \param user + +void QXmppCallManager::setTurnUser(const QString &user) +{ + d->turnUser = user; +} + +/// Sets the \a password used for authentication with the TURN server. +/// +/// \param password + +void QXmppCallManager::setTurnPassword(const QString &password) +{ + d->turnPassword = password; +} + +/// Handles call destruction. + +void QXmppCallManager::_q_callDestroyed(QObject *object) { d->calls.removeAll(static_cast<QXmppCall*>(object)); } -/// Handles acknowledgements +/// Handles disconnection from server. + +void QXmppCallManager::_q_disconnected() +{ + foreach (QXmppCall *call, d->calls) + call->d->terminate(QXmppJingleIq::Reason::Gone); +} + +/// Handles acknowledgements. /// -void QXmppCallManager::iqReceived(const QXmppIq &ack) +void QXmppCallManager::_q_iqReceived(const QXmppIq &ack) { if (ack.type() != QXmppIq::Result) return; @@ -848,10 +914,10 @@ void QXmppCallManager::iqReceived(const QXmppIq &ack) call->d->handleAck(ack); } -/// Handle Jingle IQs. +/// Handles a Jingle IQ. /// -void QXmppCallManager::jingleIqReceived(const QXmppJingleIq &iq) +void QXmppCallManager::_q_jingleIqReceived(const QXmppJingleIq &iq) { if (iq.type() != QXmppIq::Set) return; @@ -912,44 +978,18 @@ void QXmppCallManager::jingleIqReceived(const QXmppJingleIq &iq) } } -/// Sets the STUN server to use to determine server-reflexive addresses -/// and ports. -/// -/// \param host The address of the STUN server. -/// \param port The port of the STUN server. - -void QXmppCallManager::setStunServer(const QHostAddress &host, quint16 port) -{ - d->stunHost = host; - d->stunPort = port; -} - -/// Sets the TURN server to use to relay packets in double-NAT configurations. -/// -/// \param host The address of the TURN server. -/// \param port The port of the TURN server. - -void QXmppCallManager::setTurnServer(const QHostAddress &host, quint16 port) -{ - d->turnHost = host; - d->turnPort = port; -} - -/// Sets the \a user used for authentication with the TURN server. -/// -/// \param user +/// Handles a presence. -void QXmppCallManager::setTurnUser(const QString &user) +void QXmppCallManager::_q_presenceReceived(const QXmppPresence &presence) { - d->turnUser = user; -} - -/// Sets the \a password used for authentication with the TURN server. -/// -/// \param password + if (presence.type() != QXmppPresence::Unavailable) + return; -void QXmppCallManager::setTurnPassword(const QString &password) -{ - d->turnPassword = password; + foreach (QXmppCall *call, d->calls) { + if (presence.from() == call->jid()) { + // the remote party has gone away, terminate call + call->d->terminate(QXmppJingleIq::Reason::Gone); + } + } } diff --git a/src/QXmppCallManager.h b/src/QXmppCallManager.h index 7ece91b2..cb309592 100644 --- a/src/QXmppCallManager.h +++ b/src/QXmppCallManager.h @@ -39,6 +39,7 @@ class QXmppIq; class QXmppJingleCandidate; class QXmppJingleIq; class QXmppJinglePayloadType; +class QXmppPresence; class QXmppRtpAudioChannel; class QXmppRtpVideoChannel; @@ -191,9 +192,11 @@ protected: /// \endcond private slots: - void callDestroyed(QObject *object); - void iqReceived(const QXmppIq &iq); - void jingleIqReceived(const QXmppJingleIq &iq); + void _q_callDestroyed(QObject *object); + void _q_disconnected(); + void _q_iqReceived(const QXmppIq &iq); + void _q_jingleIqReceived(const QXmppJingleIq &iq); + void _q_presenceReceived(const QXmppPresence &presence); private: QXmppCallManagerPrivate *d; diff --git a/src/QXmppCodec.cpp b/src/QXmppCodec.cpp index 7daa00d5..a9e16cfc 100644 --- a/src/QXmppCodec.cpp +++ b/src/QXmppCodec.cpp @@ -43,6 +43,14 @@ #include <theora/theoraenc.h> #endif +#ifdef QXMPP_USE_VPX +#define VPX_CODEC_DISABLE_COMPAT 1 +#include <vpx/vpx_decoder.h> +#include <vpx/vpx_encoder.h> +#include <vpx/vp8cx.h> +#include <vpx/vp8dx.h> +#endif + #define BIAS (0x84) /* Bias for linear code. */ #define CLIP 8159 @@ -52,6 +60,13 @@ #define SEG_SHIFT (4) /* Left shift for segment number. */ #define SEG_MASK (0x70) /* Segment field mask. */ +enum FragmentType { + NoFragment = 0, + StartFragment, + MiddleFragment, + EndFragment, +}; + static qint16 seg_aend[8] = {0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF}; static qint16 seg_uend[8] = {0x3F, 0x7F, 0xFF, 0x1FF, @@ -376,6 +391,9 @@ public: bool QXmppTheoraDecoderPrivate::decodeFrame(const QByteArray &buffer, QXmppVideoFrame *frame) { + if (!ctx) + return false; + ogg_packet packet; packet.packet = (unsigned char*) buffer.data(); packet.bytes = buffer.size(); @@ -487,12 +505,12 @@ QXmppVideoFormat QXmppTheoraDecoder::format() const return format; } -QList<QXmppVideoFrame> QXmppTheoraDecoder::handlePacket(const QByteArray &ba) +QList<QXmppVideoFrame> QXmppTheoraDecoder::handlePacket(const QXmppRtpPacket &packet) { QList<QXmppVideoFrame> frames; // theora deframing: draft-ietf-avt-rtp-theora-00 - QDataStream stream(ba); + QDataStream stream(packet.payload); quint32 theora_header; stream >> theora_header; @@ -511,7 +529,7 @@ QList<QXmppVideoFrame> QXmppTheoraDecoder::handlePacket(const QByteArray &ba) QXmppVideoFrame frame; quint16 packetLength; - if (theora_frag == 0) { + if (theora_frag == NoFragment) { // unfragmented packet(s) for (int i = 0; i < theora_packets; ++i) { stream >> packetLength; @@ -522,7 +540,7 @@ QList<QXmppVideoFrame> QXmppTheoraDecoder::handlePacket(const QByteArray &ba) d->packetBuffer.resize(packetLength); stream.readRawData(d->packetBuffer.data(), packetLength); - if (d->ctx && d->decodeFrame(d->packetBuffer, &frame)) + if (d->decodeFrame(d->packetBuffer, &frame)) frames << frame; d->packetBuffer.resize(0); } @@ -535,7 +553,7 @@ QList<QXmppVideoFrame> QXmppTheoraDecoder::handlePacket(const QByteArray &ba) } int pos; - if (theora_frag == 1) { + if (theora_frag == StartFragment) { // start fragment pos = 0; d->packetBuffer.resize(packetLength); @@ -546,9 +564,9 @@ QList<QXmppVideoFrame> QXmppTheoraDecoder::handlePacket(const QByteArray &ba) } stream.readRawData(d->packetBuffer.data() + pos, packetLength); - if (theora_frag == 3) { + if (theora_frag == EndFragment) { // end fragment - if (d->ctx && d->decodeFrame(d->packetBuffer, &frame)) + if (d->decodeFrame(d->packetBuffer, &frame)) frames << frame; d->packetBuffer.resize(0); } @@ -641,6 +659,8 @@ bool QXmppTheoraDecoder::setParameters(const QMap<QString, QString> ¶meters) qWarning("Theora configuration did not contain enough headers"); return false; } + +#ifdef QXMPP_DEBUG_THEORA qDebug("Theora frame_width %i, frame_height %i, colorspace %i, pixel_fmt: %i, target_bitrate: %i, quality: %i, keyframe_granule_shift: %i", d->info.frame_width, d->info.frame_height, @@ -649,6 +669,7 @@ bool QXmppTheoraDecoder::setParameters(const QMap<QString, QString> ¶meters) d->info.target_bitrate, d->info.quality, d->info.keyframe_granule_shift); +#endif if (d->info.pixel_fmt != TH_PF_420 && d->info.pixel_fmt != TH_PF_422) { qWarning("Theora frames have an unsupported pixel format %d", d->info.pixel_fmt); return false; @@ -666,7 +687,7 @@ bool QXmppTheoraDecoder::setParameters(const QMap<QString, QString> ¶meters) class QXmppTheoraEncoderPrivate { public: - void writeFrame(QDataStream &stream, quint8 theora_frag, quint8 theora_packets, const char *data, quint16 length); + void writeFragment(QDataStream &stream, FragmentType frag_type, quint8 theora_packets, const char *data, quint16 length); th_comment comment; th_info info; @@ -679,12 +700,12 @@ public: QByteArray ident; }; -void QXmppTheoraEncoderPrivate::writeFrame(QDataStream &stream, quint8 theora_frag, quint8 theora_packets, const char *data, quint16 length) +void QXmppTheoraEncoderPrivate::writeFragment(QDataStream &stream, FragmentType frag_type, quint8 theora_packets, const char *data, quint16 length) { - // raw data - const quint8 theora_type = 0; + // theora framing: draft-ietf-avt-rtp-theora-00 + const quint8 theora_type = 0; // raw data stream.writeRawData(ident.constData(), ident.size()); - stream << quint8(((theora_frag << 6) & 0xc0) | + stream << quint8(((frag_type << 6) & 0xc0) | ((theora_type << 4) & 0x30) | (theora_packets & 0x0f)); stream << quint16(length); @@ -717,7 +738,7 @@ bool QXmppTheoraEncoder::setFormat(const QXmppVideoFormat &format) const QXmppVideoFrame::PixelFormat pixelFormat = format.pixelFormat(); if ((pixelFormat != QXmppVideoFrame::Format_YUV420P) && (pixelFormat != QXmppVideoFrame::Format_YUYV)) { - qWarning("Theora decoder does not support the given format"); + qWarning("Theora encoder does not support the given format"); return false; } @@ -833,7 +854,7 @@ QList<QByteArray> QXmppTheoraEncoder::handleFrame(const QXmppVideoFrame &frame) d->ycbcr_buffer[1].data = d->ycbcr_buffer[0].data + d->ycbcr_buffer[0].stride * d->ycbcr_buffer[0].height; d->ycbcr_buffer[2].stride = d->ycbcr_buffer[1].stride; d->ycbcr_buffer[2].data = d->ycbcr_buffer[1].data + d->ycbcr_buffer[1].stride * d->ycbcr_buffer[1].height; - } else if (d->info.pixel_fmt = TH_PF_422) { + } else if (d->info.pixel_fmt == TH_PF_422) { // YUV 4:2:2 unpacking const int width = frame.width(); const int height = frame.height(); @@ -871,24 +892,23 @@ QList<QByteArray> QXmppTheoraEncoder::handleFrame(const QXmppVideoFrame &frame) QDataStream stream(&payload, QIODevice::WriteOnly); const char *data = (const char*) packet.packet; int size = packet.bytes; - quint8 theora_frag = 0; if (size <= PACKET_MAX) { // no fragmentation stream.device()->reset(); payload.resize(0); - d->writeFrame(stream, theora_frag, 1, data, size); + d->writeFragment(stream, NoFragment, 1, data, size); packets << payload; } else { // fragmentation - theora_frag = 1; + FragmentType frag_type = StartFragment; while (size) { const int length = qMin(PACKET_MAX, size); stream.device()->reset(); payload.resize(0); - d->writeFrame(stream, theora_frag, 0, data, length); + d->writeFragment(stream, frag_type, 0, data, length); data += length; size -= length; - theora_frag = (size > PACKET_MAX) ? 2 : 3; + frag_type = (size > PACKET_MAX) ? MiddleFragment : EndFragment; packets << payload; } } @@ -908,3 +928,291 @@ QMap<QString, QString> QXmppTheoraEncoder::parameters() const } #endif + +#ifdef QXMPP_USE_VPX + +class QXmppVpxDecoderPrivate +{ +public: + bool decodeFrame(const QByteArray &buffer, QXmppVideoFrame *frame); + + vpx_codec_ctx_t codec; + QByteArray packetBuffer; +}; + +bool QXmppVpxDecoderPrivate::decodeFrame(const QByteArray &buffer, QXmppVideoFrame *frame) +{ + if (vpx_codec_decode(&codec, (const uint8_t*)buffer.constData(), buffer.size(), NULL, 0) != VPX_CODEC_OK) { + qWarning("Vpx packet could not be decoded: %s", vpx_codec_error_detail(&codec)); + return false; + } + + vpx_codec_iter_t iter = NULL; + vpx_image_t *img; + while ((img = vpx_codec_get_frame(&codec, &iter))) { + if (img->fmt == VPX_IMG_FMT_I420) { + if (!frame->isValid()) { + const int bytes = img->d_w * img->d_h * 3 / 2; + + *frame = QXmppVideoFrame(bytes, + QSize(img->d_w, img->d_h), + img->d_w, + QXmppVideoFrame::Format_YUV420P); + } + uchar *output = frame->bits(); + + for (int i = 0; i < 3; ++i) { + uchar *input = img->planes[i]; + const int div = (i == 0) ? 1 : 2; + for (unsigned int y = 0; y < img->d_h / div; ++y) { + memcpy(output, input, img->d_w / div); + input += img->stride[i]; + output += img->d_w / div; + } + } + } else { + qWarning("Vpx decoder received an unsupported frame format: %d", img->fmt); + } + } + + return true; +} + +QXmppVpxDecoder::QXmppVpxDecoder() +{ + d = new QXmppVpxDecoderPrivate; + if (vpx_codec_dec_init(&d->codec, vpx_codec_vp8_dx(), NULL, 0) != VPX_CODEC_OK) { + qWarning("Vpx decoder could not be initialised"); + } +} + +QXmppVpxDecoder::~QXmppVpxDecoder() +{ + vpx_codec_destroy(&d->codec); + delete d; +} + +QXmppVideoFormat QXmppVpxDecoder::format() const +{ + QXmppVideoFormat format; + format.setFrameRate(15.0); + format.setFrameSize(QSize(320, 240)); + format.setPixelFormat(QXmppVideoFrame::Format_YUV420P); + return format; +} + +QList<QXmppVideoFrame> QXmppVpxDecoder::handlePacket(const QXmppRtpPacket &packet) +{ + QList<QXmppVideoFrame> frames; + + // vp8 deframing: http://tools.ietf.org/html/draft-westin-payload-vp8-00 + QDataStream stream(packet.payload); + quint8 vpx_header; + stream >> vpx_header; + + const bool have_id = (vpx_header & 0x10) != 0; + const quint8 frag_type = (vpx_header & 0x6) >> 1; + if (have_id) { + qWarning("Vpx decoder does not support pictureId yet"); + return frames; + } + + const int packetLength = packet.payload.size() - 1; +#ifdef QXMPP_DEBUG_VPX + qDebug("Vpx fragment FI: %d, size %d", frag_type, packetLength); +#endif + + QXmppVideoFrame frame; + + if (frag_type == NoFragment) { + // unfragmented packet + if (d->decodeFrame(packet.payload.mid(1), &frame)) + frames << frame; + d->packetBuffer.resize(0); + } else { + // fragments + if (frag_type == StartFragment) { + // start fragment + d->packetBuffer = packet.payload.mid(1); + } else { + // continuation or end fragment + const int packetPos = d->packetBuffer.size(); + d->packetBuffer.resize(packetPos + packetLength); + stream.readRawData(d->packetBuffer.data() + packetPos, packetLength); + } + + if (frag_type == EndFragment) { + // end fragment + if (d->decodeFrame(d->packetBuffer, &frame)) + frames << frame; + d->packetBuffer.resize(0); + } + + } + + return frames; +} + +bool QXmppVpxDecoder::setParameters(const QMap<QString, QString> ¶meters) +{ + return true; +} + +class QXmppVpxEncoderPrivate +{ +public: + void writeFragment(QDataStream &stream, FragmentType frag_type, const char *data, quint16 length); + + vpx_codec_ctx_t codec; + vpx_codec_enc_cfg_t cfg; + vpx_image_t *imageBuffer; + int frameCount; +}; + +void QXmppVpxEncoderPrivate::writeFragment(QDataStream &stream, FragmentType frag_type, const char *data, quint16 length) +{ + // vp8 framing: http://tools.ietf.org/html/draft-westin-payload-vp8-00 +#ifdef QXMPP_DEBUG_VPX + qDebug("Vpx encoder writing packet frag: %i, size: %u", frag_type, length); +#endif + stream << quint8(((frag_type << 1) & 0x6) | + (frag_type == NoFragment || frag_type == StartFragment)); + stream.writeRawData(data, length); +} + +QXmppVpxEncoder::QXmppVpxEncoder() +{ + d = new QXmppVpxEncoderPrivate; + d->frameCount = 0; + d->imageBuffer = 0; + vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &d->cfg, 0); +} + +QXmppVpxEncoder::~QXmppVpxEncoder() +{ + vpx_codec_destroy(&d->codec); + if (d->imageBuffer) + vpx_img_free(d->imageBuffer); + delete d; +} + +bool QXmppVpxEncoder::setFormat(const QXmppVideoFormat &format) +{ + const QXmppVideoFrame::PixelFormat pixelFormat = format.pixelFormat(); + if (pixelFormat != QXmppVideoFrame::Format_YUYV) { + qWarning("Vpx encoder does not support the given format"); + return false; + } + + d->cfg.rc_target_bitrate = format.frameSize().width() * format.frameSize().height() * d->cfg.rc_target_bitrate / d->cfg.g_w / d->cfg.g_h; + d->cfg.g_w = format.frameSize().width(); + d->cfg.g_h = format.frameSize().height(); + if (vpx_codec_enc_init(&d->codec, vpx_codec_vp8_cx(), &d->cfg, 0) != VPX_CODEC_OK) { + qWarning("Vpx encoder could not be initialised"); + return false; + } + + d->imageBuffer = vpx_img_alloc(NULL, VPX_IMG_FMT_I420, + format.frameSize().width(), format.frameSize().height(), 1); + return true; +} + +QList<QByteArray> QXmppVpxEncoder::handleFrame(const QXmppVideoFrame &frame) +{ + const int PACKET_MAX = 1388; + QList<QByteArray> packets; + + // try to encode frame + if (frame.pixelFormat() == QXmppVideoFrame::Format_YUYV) { + // YUYV -> YUV420P + const int width = frame.width(); + const int height = frame.height(); + const int stride = frame.bytesPerLine(); + const uchar *row = frame.bits(); + uchar *y_row = d->imageBuffer->planes[VPX_PLANE_Y]; + uchar *cb_row = d->imageBuffer->planes[VPX_PLANE_U]; + uchar *cr_row = d->imageBuffer->planes[VPX_PLANE_V]; + for (int y = 0; y < height; y += 2) { + // odd row + const uchar *ptr = row; + uchar *y_out = y_row; + uchar *cb_out = cb_row; + uchar *cr_out = cr_row; + for (int x = 0; x < width; x += 2) { + *(y_out++) = *(ptr++); + *(cb_out++) = *(ptr++); + *(y_out++) = *(ptr++); + *(cr_out++) = *(ptr++); + } + row += stride; + y_row += d->imageBuffer->stride[VPX_PLANE_Y]; + cb_row += d->imageBuffer->stride[VPX_PLANE_U]; + cr_row += d->imageBuffer->stride[VPX_PLANE_V]; + + // even row + ptr = row; + y_out = y_row; + for (int x = 0; x < width; x += 2) { + *(y_out++) = *(ptr++); + ptr++; + *(y_out++) = *(ptr++); + ptr++; + } + row += stride; + y_row += d->imageBuffer->stride[VPX_PLANE_Y]; + } + } else { + qWarning("Vpx encoder does not support the given format"); + return packets; + } + + if (vpx_codec_encode(&d->codec, d->imageBuffer, d->frameCount, 1, 0, VPX_DL_REALTIME) != VPX_CODEC_OK) { + qWarning("Vpx encoder could not handle frame: %s", vpx_codec_error_detail(&d->codec)); + return packets; + } + + // extract data + QByteArray payload; + vpx_codec_iter_t iter = NULL; + const vpx_codec_cx_pkt_t *pkt; + while ((pkt = vpx_codec_get_cx_data(&d->codec, &iter))) { + if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { +#ifdef QXMPP_DEBUG_VPX + qDebug("Vpx encoded packet %lu bytes", pkt->data.frame.sz); +#endif + QDataStream stream(&payload, QIODevice::WriteOnly); + const char *data = (const char*) pkt->data.frame.buf; + int size = pkt->data.frame.sz; + if (size <= PACKET_MAX) { + // no fragmentation + stream.device()->reset(); + payload.resize(0); + d->writeFragment(stream, NoFragment, data, size); + packets << payload; + } else { + // fragmentation + FragmentType frag_type = StartFragment; + while (size) { + const int length = qMin(PACKET_MAX, size); + stream.device()->reset(); + payload.resize(0); + d->writeFragment(stream, frag_type, data, length); + data += length; + size -= length; + frag_type = (size > PACKET_MAX) ? MiddleFragment : EndFragment; + packets << payload; + } + } + } + } + d->frameCount++; + + return packets; +} + +QMap<QString, QString> QXmppVpxEncoder::parameters() const +{ + return QMap<QString, QString>(); +} + +#endif diff --git a/src/QXmppCodec.h b/src/QXmppCodec.h index 1d52cdbd..61dd45a2 100644 --- a/src/QXmppCodec.h +++ b/src/QXmppCodec.h @@ -26,6 +26,7 @@ #include <QtGlobal> +class QXmppRtpPacket; class QXmppVideoFormat; class QXmppVideoFrame; @@ -110,7 +111,7 @@ class QXmppVideoDecoder { public: virtual QXmppVideoFormat format() const = 0; - virtual QList<QXmppVideoFrame> handlePacket(const QByteArray &ba) = 0; + virtual QList<QXmppVideoFrame> handlePacket(const QXmppRtpPacket &packet) = 0; virtual bool setParameters(const QMap<QString, QString> ¶meters) = 0; }; @@ -133,7 +134,7 @@ public: ~QXmppTheoraDecoder(); QXmppVideoFormat format() const; - QList<QXmppVideoFrame> handlePacket(const QByteArray &ba); + QList<QXmppVideoFrame> handlePacket(const QXmppRtpPacket &packet); bool setParameters(const QMap<QString, QString> ¶meters); private: @@ -155,4 +156,37 @@ private: }; #endif +#ifdef QXMPP_USE_VPX +class QXmppVpxDecoderPrivate; +class QXmppVpxEncoderPrivate; + +class QXmppVpxDecoder : public QXmppVideoDecoder +{ +public: + QXmppVpxDecoder(); + ~QXmppVpxDecoder(); + + QXmppVideoFormat format() const; + QList<QXmppVideoFrame> handlePacket(const QXmppRtpPacket &packet); + bool setParameters(const QMap<QString, QString> ¶meters); + +private: + QXmppVpxDecoderPrivate *d; +}; + +class QXmppVpxEncoder : public QXmppVideoEncoder +{ +public: + QXmppVpxEncoder(); + ~QXmppVpxEncoder(); + + bool setFormat(const QXmppVideoFormat &format); + QList<QByteArray> handleFrame(const QXmppVideoFrame &frame); + QMap<QString, QString> parameters() const; + +private: + QXmppVpxEncoderPrivate *d; +}; +#endif + #endif diff --git a/src/QXmppRtpChannel.cpp b/src/QXmppRtpChannel.cpp index 732246eb..df26bc30 100644 --- a/src/QXmppRtpChannel.cpp +++ b/src/QXmppRtpChannel.cpp @@ -863,11 +863,26 @@ QXmppRtpVideoChannel::QXmppRtpVideoChannel(QObject *parent) d->outgoingFormat.setPixelFormat(QXmppVideoFrame::Format_YUYV); // set supported codecs -#ifdef QXMPP_USE_THEORA - QXmppVideoEncoder *encoder = new QXmppTheoraEncoder; - encoder->setFormat(d->outgoingFormat); + 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()); @@ -910,7 +925,7 @@ void QXmppRtpVideoChannel::datagramReceived(const QByteArray &ba) QXmppVideoDecoder *decoder = d->decoders.value(packet.type); if (!decoder) return; - d->frames << decoder->handlePacket(packet.payload); + d->frames << decoder->handlePacket(packet); } QXmppVideoFormat QXmppRtpVideoChannel::decoderFormat() const @@ -951,10 +966,16 @@ void QXmppRtpVideoChannel::payloadTypesChanged() d->decoders.clear(); foreach (const QXmppJinglePayloadType &payload, m_incomingPayloadTypes) { QXmppVideoDecoder *decoder = 0; + if (false) + {} #ifdef QXMPP_USE_THEORA - if (payload.name().toLower() == "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); @@ -968,10 +989,17 @@ void QXmppRtpVideoChannel::payloadTypesChanged() } foreach (const QXmppJinglePayloadType &payload, m_outgoingPayloadTypes) { QXmppVideoEncoder *encoder = 0; + if (false) + {} #ifdef QXMPP_USE_THEORA - if (payload.name().toLower() == "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; diff --git a/src/QXmppRtpChannel.h b/src/QXmppRtpChannel.h index 1dd8c7a5..0af6596c 100644 --- a/src/QXmppRtpChannel.h +++ b/src/QXmppRtpChannel.h @@ -180,6 +180,7 @@ public: Format_RGB32 = 3, Format_RGB24 = 4, Format_YUV420P = 18, + Format_UYVY = 20, Format_YUYV = 21, }; diff --git a/src/src.pro b/src/src.pro index cc702b1a..1c9f88de 100644 --- a/src/src.pro +++ b/src/src.pro @@ -16,6 +16,10 @@ LIBS += $$QXMPP_INTERNAL_LIBS # DEFINES += QXMPP_USE_THEORA # LIBS += -ltheoradec -ltheoraenc +# To enable support for the Vpx video codec, uncomment the following: +# DEFINES += QXMPP_USE_VPX +# LIBS += -lvpx + # Target definition TARGET = $$QXMPP_LIBRARY_NAME VERSION = $$QXMPP_VERSION |
