From 90036fc2cf5918c028f043edff7f5d38d1efb4cc Mon Sep 17 00:00:00 2001 From: Niels Ole Salscheider Date: Fri, 17 May 2019 14:30:02 -0700 Subject: Port QXmppCallManager to use GStreamer --- src/base/QXmppCodec.cpp | 1433 ------------------------------------------ src/base/QXmppCodec_p.h | 243 ------- src/base/QXmppRtcpPacket.cpp | 660 ------------------- src/base/QXmppRtcpPacket.h | 169 ----- src/base/QXmppRtpChannel.cpp | 999 ----------------------------- src/base/QXmppRtpChannel.h | 304 --------- src/base/QXmppRtpPacket.cpp | 224 ------- src/base/QXmppRtpPacket.h | 75 --- 8 files changed, 4107 deletions(-) delete mode 100644 src/base/QXmppCodec.cpp delete mode 100644 src/base/QXmppCodec_p.h delete mode 100644 src/base/QXmppRtcpPacket.cpp delete mode 100644 src/base/QXmppRtcpPacket.h delete mode 100644 src/base/QXmppRtpChannel.cpp delete mode 100644 src/base/QXmppRtpChannel.h delete mode 100644 src/base/QXmppRtpPacket.cpp delete mode 100644 src/base/QXmppRtpPacket.h (limited to 'src/base') diff --git a/src/base/QXmppCodec.cpp b/src/base/QXmppCodec.cpp deleted file mode 100644 index d6396bee..00000000 --- a/src/base/QXmppCodec.cpp +++ /dev/null @@ -1,1433 +0,0 @@ -/* - * Copyright (C) 2008-2020 The QXmpp developers - * - * Author: - * Jeremy Lainé - * - * Source: - * https://github.com/qxmpp-project/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. - * - */ - -/* - * G.711 based on reference implementation by Sun Microsystems, Inc. - */ - -#include "QXmppCodec_p.h" -#include "QXmppRtpChannel.h" -#include "QXmppRtpPacket.h" - -#include - -#include -#include -#include -#include - -#ifdef QXMPP_USE_SPEEX -#include -#endif - -#ifdef QXMPP_USE_OPUS -#include -#endif - -#ifdef QXMPP_USE_THEORA -#include -#include -#endif - -#ifdef QXMPP_USE_VPX -#define VPX_CODEC_DISABLE_COMPAT 1 -#include -#include -#include -#include -#endif - -#define BIAS (0x84) /* Bias for linear code. */ -#define CLIP 8159 - -#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ -#define QUANT_MASK (0xf) /* Quantization field mask. */ -#define NSEGS (8) /* Number of A-law segments. */ -#define SEG_SHIFT (4) /* Left shift for segment number. */ -#define SEG_MASK (0x70) /* Segment field mask. */ - -// Distance (in frames) between two key frames (video only). -#define GOPSIZE 32 - -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, - 0x3FF, 0x7FF, 0xFFF, 0x1FFF }; - -static qint16 search(qint16 val, qint16 *table, qint16 size) -{ - qint16 i; - - for (i = 0; i < size; i++) { - if (val <= *table++) - return (i); - } - return (size); -} - -/* - * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law - * - * Accepts a 16-bit integer and encodes it as A-law data. - * - * Linear Input Code Compressed Code - * ------------------------ --------------- - * 0000000wxyza 000wxyz - * 0000001wxyza 001wxyz - * 000001wxyzab 010wxyz - * 00001wxyzabc 011wxyz - * 0001wxyzabcd 100wxyz - * 001wxyzabcde 101wxyz - * 01wxyzabcdef 110wxyz - * 1wxyzabcdefg 111wxyz - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - */ -static quint8 linear2alaw(qint16 pcm_val) -{ - qint16 mask; - qint16 seg; - quint8 aval; - - pcm_val = pcm_val >> 3; - - if (pcm_val >= 0) { - mask = 0xD5; /* sign (7th) bit = 1 */ - } else { - mask = 0x55; /* sign bit = 0 */ - pcm_val = -pcm_val - 1; - } - - /* Convert the scaled magnitude to segment number. */ - seg = search(pcm_val, seg_aend, 8); - - /* Combine the sign, segment, and quantization bits. */ - - if (seg >= 8) /* out of range, return maximum value. */ - return (quint8)(0x7F ^ mask); - else { - aval = (quint8)seg << SEG_SHIFT; - if (seg < 2) - aval |= (pcm_val >> 1) & QUANT_MASK; - else - aval |= (pcm_val >> seg) & QUANT_MASK; - return (aval ^ mask); - } -} - -/* - * alaw2linear() - Convert an A-law value to 16-bit linear PCM - * - */ -static qint16 alaw2linear(quint8 a_val) -{ - qint16 t; - qint16 seg; - - a_val ^= 0x55; - - t = (a_val & QUANT_MASK) << 4; - seg = ((qint16)a_val & SEG_MASK) >> SEG_SHIFT; - switch (seg) { - case 0: - t += 8; - break; - case 1: - t += 0x108; - break; - default: - t += 0x108; - t <<= seg - 1; - } - return ((a_val & SIGN_BIT) ? t : -t); -} - -/* - * linear2ulaw() - Convert a linear PCM value to u-law - * - * In order to simplify the encoding process, the original linear magnitude - * is biased by adding 33 which shifts the encoding range from (0 - 8158) to - * (33 - 8191). The result can be seen in the following encoding table: - * - * Biased Linear Input Code Compressed Code - * ------------------------ --------------- - * 00000001wxyza 000wxyz - * 0000001wxyzab 001wxyz - * 000001wxyzabc 010wxyz - * 00001wxyzabcd 011wxyz - * 0001wxyzabcde 100wxyz - * 001wxyzabcdef 101wxyz - * 01wxyzabcdefg 110wxyz - * 1wxyzabcdefgh 111wxyz - * - * Each biased linear code has a leading 1 which identifies the segment - * number. The value of the segment number is equal to 7 minus the number - * of leading 0's. The quantization interval is directly available as the - * four bits wxyz. * The trailing bits (a - h) are ignored. - * - * Ordinarily the complement of the resulting code word is used for - * transmission, and so the code word is complemented before it is returned. - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - */ -static quint8 linear2ulaw(qint16 pcm_val) -{ - qint16 mask; - qint16 seg; - quint8 uval; - - /* Get the sign and the magnitude of the value. */ - pcm_val = pcm_val >> 2; - if (pcm_val < 0) { - pcm_val = -pcm_val; - mask = 0x7F; - } else { - mask = 0xFF; - } - if (pcm_val > CLIP) - pcm_val = CLIP; /* clip the magnitude */ - pcm_val += (BIAS >> 2); - - /* Convert the scaled magnitude to segment number. */ - seg = search(pcm_val, seg_uend, 8); - - /* - * Combine the sign, segment, quantization bits; - * and complement the code word. - */ - if (seg >= 8) /* out of range, return maximum value. */ - return (quint8)(0x7F ^ mask); - else { - uval = (quint8)(seg << 4) | ((pcm_val >> (seg + 1)) & 0xF); - return (uval ^ mask); - } -} - -/* - * ulaw2linear() - Convert a u-law value to 16-bit linear PCM - * - * First, a biased linear code is derived from the code word. An unbiased - * output can then be obtained by subtracting 33 from the biased code. - * - * Note that this function expects to be passed the complement of the - * original code word. This is in keeping with ISDN conventions. - */ -static qint16 ulaw2linear(quint8 u_val) -{ - qint16 t; - - /* Complement to obtain normal u-law value. */ - u_val = ~u_val; - - /* - * Extract and bias the quantization bits. Then - * shift up by the segment number and subtract out the bias. - */ - t = ((u_val & QUANT_MASK) << 3) + BIAS; - t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; - - return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); -} - -QXmppCodec::~QXmppCodec() -{ -} - -QXmppVideoDecoder::~QXmppVideoDecoder() -{ -} - -QXmppVideoEncoder::~QXmppVideoEncoder() -{ -} - -QXmppG711aCodec::QXmppG711aCodec(int clockrate) -{ - m_frequency = clockrate; -} - -qint64 QXmppG711aCodec::encode(QDataStream &input, QDataStream &output) -{ - qint64 samples = 0; - qint16 pcm; - while (!input.atEnd()) { - input >> pcm; - output << linear2alaw(pcm); - ++samples; - } - return samples; -} - -qint64 QXmppG711aCodec::decode(QDataStream &input, QDataStream &output) -{ - qint64 samples = 0; - quint8 g711; - while (!input.atEnd()) { - input >> g711; - output << alaw2linear(g711); - ++samples; - } - return samples; -} - -QXmppG711uCodec::QXmppG711uCodec(int clockrate) -{ - m_frequency = clockrate; -} - -qint64 QXmppG711uCodec::encode(QDataStream &input, QDataStream &output) -{ - qint64 samples = 0; - qint16 pcm; - while (!input.atEnd()) { - input >> pcm; - output << linear2ulaw(pcm); - ++samples; - } - return samples; -} - -qint64 QXmppG711uCodec::decode(QDataStream &input, QDataStream &output) -{ - qint64 samples = 0; - quint8 g711; - while (!input.atEnd()) { - input >> g711; - output << ulaw2linear(g711); - ++samples; - } - return samples; -} - -#ifdef QXMPP_USE_SPEEX -QXmppSpeexCodec::QXmppSpeexCodec(int clockrate) -{ - const SpeexMode *mode = &speex_nb_mode; - if (clockrate == 32000) - mode = &speex_uwb_mode; - else if (clockrate == 16000) - mode = &speex_wb_mode; - else if (clockrate == 8000) - mode = &speex_nb_mode; - else - qWarning() << "QXmppSpeexCodec got invalid clockrate" << clockrate; - - // encoder - encoder_bits = new SpeexBits; - speex_bits_init(encoder_bits); - encoder_state = speex_encoder_init(mode); - - // decoder - decoder_bits = new SpeexBits; - speex_bits_init(decoder_bits); - decoder_state = speex_decoder_init(mode); - - // get frame size in samples - speex_encoder_ctl(encoder_state, SPEEX_GET_FRAME_SIZE, &frame_samples); -} - -QXmppSpeexCodec::~QXmppSpeexCodec() -{ - delete encoder_bits; - delete decoder_bits; -} - -qint64 QXmppSpeexCodec::encode(QDataStream &input, QDataStream &output) -{ - QByteArray pcm_buffer(frame_samples * 2, 0); - const int length = input.readRawData(pcm_buffer.data(), pcm_buffer.size()); - if (length != pcm_buffer.size()) { - qWarning() << "Read only read" << length << "bytes"; - return 0; - } - speex_bits_reset(encoder_bits); - speex_encode_int(encoder_state, (short *)pcm_buffer.data(), encoder_bits); - QByteArray speex_buffer(speex_bits_nbytes(encoder_bits), 0); - speex_bits_write(encoder_bits, speex_buffer.data(), speex_buffer.size()); - output.writeRawData(speex_buffer.data(), speex_buffer.size()); - return frame_samples; -} - -qint64 QXmppSpeexCodec::decode(QDataStream &input, QDataStream &output) -{ - const int length = input.device()->bytesAvailable(); - QByteArray speex_buffer(length, 0); - input.readRawData(speex_buffer.data(), speex_buffer.size()); - speex_bits_read_from(decoder_bits, speex_buffer.data(), speex_buffer.size()); - QByteArray pcm_buffer(frame_samples * 2, 0); - speex_decode_int(decoder_state, decoder_bits, (short *)pcm_buffer.data()); - output.writeRawData(pcm_buffer.data(), pcm_buffer.size()); - return frame_samples; -} - -#endif - -#ifdef QXMPP_USE_OPUS -QXmppOpusCodec::QXmppOpusCodec(int clockrate, int channels) : sampleRate(clockrate), - nChannels(channels) -{ - int error; - encoder = opus_encoder_create(clockrate, channels, OPUS_APPLICATION_VOIP, &error); - - if (encoder || error == OPUS_OK) { - // Add some options for error correction. - opus_encoder_ctl(encoder, OPUS_SET_INBAND_FEC(1)); - opus_encoder_ctl(encoder, OPUS_SET_PACKET_LOSS_PERC(20)); - opus_encoder_ctl(encoder, OPUS_SET_DTX(1)); -#ifdef OPUS_SET_PREDICTION_DISABLED - opus_encoder_ctl(encoder, OPUS_SET_PREDICTION_DISABLED(1)); -#endif - } else - qCritical() << "Opus encoder initialization error:" << opus_strerror(error); - - // Here, clockrate is synonym of sampleRate. - decoder = opus_decoder_create(clockrate, channels, &error); - - if (!encoder || error != OPUS_OK) - qCritical() << "Opus decoder initialization error:" << opus_strerror(error); - - // Opus only supports fixed frame durations from 2.5ms to 60ms. - // - // NOTE: https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__encoder.html - validFrameSize << 2.5e-3 << 5e-3 << 10e-3 << 20e-3 << 40e-3 << 60e-3; - - // so now, calculate the equivalent number of samples to process in each - // frame. - // - // nSamples = t * sampleRate - for (int i = 0; i < validFrameSize.size(); i++) - validFrameSize[i] *= clockrate; - - // Maxmimum number of samples for the audio buffer. - nSamples = validFrameSize.last(); -} - -QXmppOpusCodec::~QXmppOpusCodec() -{ - if (encoder) { - opus_encoder_destroy(encoder); - encoder = NULL; - } - - if (decoder) { - opus_decoder_destroy(decoder); - decoder = NULL; - } -} - -qint64 QXmppOpusCodec::encode(QDataStream &input, QDataStream &output) -{ - // Read an audio frame. - QByteArray pcm_buffer(input.device()->bytesAvailable(), 0); - int length = input.readRawData(pcm_buffer.data(), pcm_buffer.size()); - - // and append it to the sample buffer. - sampleBuffer.append(pcm_buffer.left(length)); - - // Get the maximum number of samples to encode. It must be a number - // accepted by the Opus encoder - int samples = readWindow(sampleBuffer.size()); - - if (samples < 1) - return 0; - - // The encoded stream is supposed to be smaller than the raw stream, so - QByteArray opus_buffer(sampleBuffer.size(), 0); - - length = opus_encode(encoder, - (opus_int16 *)sampleBuffer.constData(), - samples, - (uchar *)opus_buffer.data(), - opus_buffer.size()); - - if (length < 1) - qWarning() << "Opus encoding error:" << opus_strerror(length); - else - // Write the encoded stream to the output. - output.writeRawData(opus_buffer.constData(), length); - - // Remove the frame from the sample buffer. - sampleBuffer.remove(0, samples * nChannels * 2); - - if (length < 1) - return 0; - - return samples; -} - -qint64 QXmppOpusCodec::decode(QDataStream &input, QDataStream &output) -{ - QByteArray opus_buffer(input.device()->bytesAvailable(), 0); - int length = input.readRawData(opus_buffer.data(), opus_buffer.size()); - - if (length < 1) - return 0; - - // Audio frame is nSamples at maximum, so - QByteArray pcm_buffer(nSamples * nChannels * 2, 0); - - // The last argumment must be 1 to enable FEC, but I don't why it results - // in a SIGSEV. - int samples = opus_decode(decoder, - (uchar *)opus_buffer.constData(), - length, - (opus_int16 *)pcm_buffer.data(), - pcm_buffer.size(), - 0); - - if (samples < 1) { - qWarning() << "Opus decoding error:" << opus_strerror(samples); - - return 0; - } - - // Write the audio frame to the output. - output.writeRawData(pcm_buffer.constData(), samples * nChannels * 2); - - return samples; -} - -int QXmppOpusCodec::readWindow(int bufferSize) -{ - // WARNING: We are expecting 2 bytes signed samples, but this is wrong since - // input stream can have a different sample formats. - - // Get the number of frames in the buffer. - int samples = bufferSize / nChannels / 2; - - // Find an appropriate number of samples to read, according to Opus specs. - for (int i = validFrameSize.size() - 1; i >= 0; i--) - if (validFrameSize[i] <= samples) - return validFrameSize[i]; - - return 0; -} - -#endif - -#ifdef QXMPP_USE_THEORA - -class QXmppTheoraDecoderPrivate -{ -public: - bool decodeFrame(const QByteArray &buffer, QXmppVideoFrame *frame); - - th_comment comment; - th_info info; - th_setup_info *setup_info; - th_dec_ctx *ctx; - - QByteArray packetBuffer; -}; - -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(); - packet.b_o_s = 1; - packet.e_o_s = 0; - packet.granulepos = -1; - packet.packetno = 0; - if (th_decode_packetin(ctx, &packet, 0) != 0) { - qWarning("Theora packet could not be decoded"); - return false; - } - - th_ycbcr_buffer ycbcr_buffer; - if (th_decode_ycbcr_out(ctx, ycbcr_buffer) != 0) { - qWarning("Theora packet has no Y'CbCr"); - return false; - } - - if (info.pixel_fmt == TH_PF_420) { - if (!frame->isValid()) { - const int bytes = ycbcr_buffer[0].stride * ycbcr_buffer[0].height + ycbcr_buffer[1].stride * ycbcr_buffer[1].height + ycbcr_buffer[2].stride * ycbcr_buffer[2].height; - - *frame = QXmppVideoFrame(bytes, - QSize(ycbcr_buffer[0].width, ycbcr_buffer[0].height), - ycbcr_buffer[0].stride, - QXmppVideoFrame::Format_YUV420P); - } - uchar *output = frame->bits(); - for (int i = 0; i < 3; ++i) { - const int length = ycbcr_buffer[i].stride * ycbcr_buffer[i].height; - memcpy(output, ycbcr_buffer[i].data, length); - output += length; - } - return true; - } else if (info.pixel_fmt == TH_PF_422) { - if (!frame->isValid()) { - const int bytes = ycbcr_buffer[0].width * ycbcr_buffer[0].height * 2; - - *frame = QXmppVideoFrame(bytes, - QSize(ycbcr_buffer[0].width, ycbcr_buffer[0].height), - ycbcr_buffer[0].width * 2, - QXmppVideoFrame::Format_YUYV); - } - - // YUV 4:2:2 packing - const int width = ycbcr_buffer[0].width; - const int height = ycbcr_buffer[0].height; - const int y_stride = ycbcr_buffer[0].stride; - const int c_stride = ycbcr_buffer[1].stride; - const uchar *y_row = ycbcr_buffer[0].data; - const uchar *cb_row = ycbcr_buffer[1].data; - const uchar *cr_row = ycbcr_buffer[2].data; - uchar *output = frame->bits(); - for (int y = 0; y < height; ++y) { - const uchar *y_ptr = y_row; - const uchar *cb_ptr = cb_row; - const uchar *cr_ptr = cr_row; - for (int x = 0; x < width; x += 2) { - *(output++) = *(y_ptr++); - *(output++) = *(cb_ptr++); - *(output++) = *(y_ptr++); - *(output++) = *(cr_ptr++); - } - y_row += y_stride; - cb_row += c_stride; - cr_row += c_stride; - } - return true; - } else { - qWarning("Theora decoder received an unsupported frame format"); - return false; - } -} - -QXmppTheoraDecoder::QXmppTheoraDecoder() -{ - d = new QXmppTheoraDecoderPrivate; - th_comment_init(&d->comment); - th_info_init(&d->info); - d->setup_info = 0; - d->ctx = 0; -} - -QXmppTheoraDecoder::~QXmppTheoraDecoder() -{ - th_comment_clear(&d->comment); - th_info_clear(&d->info); - if (d->setup_info) - th_setup_free(d->setup_info); - if (d->ctx) - th_decode_free(d->ctx); - delete d; -} - -QXmppVideoFormat QXmppTheoraDecoder::format() const -{ - QXmppVideoFormat format; - format.setFrameSize(QSize(d->info.frame_width, d->info.frame_height)); - if (d->info.pixel_fmt == TH_PF_420) - format.setPixelFormat(QXmppVideoFrame::Format_YUV420P); - else if (d->info.pixel_fmt == TH_PF_422) - format.setPixelFormat(QXmppVideoFrame::Format_YUYV); - else - format.setPixelFormat(QXmppVideoFrame::Format_Invalid); - if (d->info.fps_denominator > 0) - format.setFrameRate(qreal(d->info.fps_numerator) / qreal(d->info.fps_denominator)); - return format; -} - -QList QXmppTheoraDecoder::handlePacket(const QXmppRtpPacket &packet) -{ - QList frames; - - // theora deframing: draft-ietf-avt-rtp-theora-00 - QDataStream stream(packet.payload()); - quint32 theora_header; - stream >> theora_header; - - quint32 theora_ident = (theora_header >> 8) & 0xffffff; - Q_UNUSED(theora_ident); - quint8 theora_frag = (theora_header & 0xc0) >> 6; - quint8 theora_type = (theora_header & 0x30) >> 4; - quint8 theora_packets = (theora_header & 0x0f); - - //qDebug("ident: 0x%08x, F: %d, TDT: %d, packets: %d", theora_ident, theora_frag, theora_type, theora_packets); - - // We only handle raw theora data - if (theora_type != 0) - return frames; - - QXmppVideoFrame frame; - quint16 packetLength; - - if (theora_frag == NoFragment) { - // unfragmented packet(s) - for (int i = 0; i < theora_packets; ++i) { - stream >> packetLength; - if (packetLength > stream.device()->bytesAvailable()) { - qWarning("Theora unfragmented packet has an invalid length"); - return frames; - } - - d->packetBuffer.resize(packetLength); - stream.readRawData(d->packetBuffer.data(), packetLength); - if (d->decodeFrame(d->packetBuffer, &frame)) - frames << frame; - d->packetBuffer.resize(0); - } - } else { - // fragments - stream >> packetLength; - if (packetLength > stream.device()->bytesAvailable()) { - qWarning("Theora packet has an invalid length"); - return frames; - } - - int pos; - if (theora_frag == StartFragment) { - // start fragment - pos = 0; - d->packetBuffer.resize(packetLength); - } else { - // continuation or end fragment - pos = d->packetBuffer.size(); - d->packetBuffer.resize(pos + packetLength); - } - stream.readRawData(d->packetBuffer.data() + pos, packetLength); - - if (theora_frag == EndFragment) { - // end fragment - if (d->decodeFrame(d->packetBuffer, &frame)) - frames << frame; - d->packetBuffer.resize(0); - } - } - return frames; -} - -bool QXmppTheoraDecoder::setParameters(const QMap ¶meters) -{ - QByteArray config = QByteArray::fromBase64(parameters.value("configuration").toLatin1()); - QDataStream stream(config); - const QIODevice *device = stream.device(); - - if (device->bytesAvailable() < 4) { - qWarning("Theora configuration is too small"); - return false; - } - - // Process packed headers - int done = 0; - quint32 header_count; - stream >> header_count; - for (quint32 i = 0; i < header_count; ++i) { - if (device->bytesAvailable() < 6) { - qWarning("Theora configuration is too small"); - return false; - } - QByteArray ident(3, 0); - quint16 length; - quint8 h_count; - - stream.readRawData(ident.data(), ident.size()); - stream >> length; - stream >> h_count; -#ifdef QXMPP_DEBUG_THEORA - qDebug("Theora packed header %u ident=%s bytes=%u count=%u", i, ident.toHex().data(), length, h_count); -#endif - - // get header sizes - QList h_sizes; - for (int h = 0; h < h_count; ++h) { - quint16 h_size = 0; - quint8 b; - do { - if (device->bytesAvailable() < 1) { - qWarning("Theora configuration is too small"); - return false; - } - stream >> b; - h_size = (h_size << 7) | (b & 0x7f); - } while (b & 0x80); - h_sizes << h_size; -#ifdef QXMPP_DEBUG_THEORA - qDebug("Theora header %d size %u", h_sizes.size() - 1, h_sizes.last()); -#endif - length -= h_size; - } - h_sizes << length; -#ifdef QXMPP_DEBUG_THEORA - qDebug("Theora header %d size %u", h_sizes.size() - 1, h_sizes.last()); -#endif - - // decode headers - ogg_packet packet; - packet.b_o_s = 1; - packet.e_o_s = 0; - packet.granulepos = -1; - packet.packetno = 0; - - for (const auto h_size : h_sizes) { - if (device->bytesAvailable() < h_size) { - qWarning("Theora configuration is too small"); - return false; - } - - packet.packet = (unsigned char *)(config.data() + device->pos()); - packet.bytes = h_size; - int ret = th_decode_headerin(&d->info, &d->comment, &d->setup_info, &packet); - if (ret < 0) { - qWarning("Theora header could not be decoded"); - return false; - } - done += ret; - stream.skipRawData(h_size); - } - } - - // check for completion - if (done < 3) { - 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, - d->info.colorspace, - d->info.pixel_fmt, - 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; - } - if (d->ctx) - th_decode_free(d->ctx); - d->ctx = th_decode_alloc(&d->info, d->setup_info); - if (!d->ctx) { - qWarning("Theora decoder could not be allocated"); - return false; - } - return true; -} - -class QXmppTheoraEncoderPrivate -{ -public: - void writeFragment(QDataStream &stream, FragmentType frag_type, quint8 theora_packets, const char *data, quint16 length); - - th_comment comment; - th_info info; - th_setup_info *setup_info; - th_enc_ctx *ctx; - th_ycbcr_buffer ycbcr_buffer; - - QByteArray buffer; - QByteArray configuration; - QByteArray ident; -}; - -void QXmppTheoraEncoderPrivate::writeFragment(QDataStream &stream, FragmentType frag_type, quint8 theora_packets, const char *data, quint16 length) -{ - // theora framing: draft-ietf-avt-rtp-theora-00 - const quint8 theora_type = 0; // raw data - stream.writeRawData(ident.constData(), ident.size()); - stream << quint8(((frag_type << 6) & 0xc0) | - ((theora_type << 4) & 0x30) | - (theora_packets & 0x0f)); - stream << quint16(length); - stream.writeRawData(data, length); -} - -QXmppTheoraEncoder::QXmppTheoraEncoder() -{ - d = new QXmppTheoraEncoderPrivate; - d->ident = QByteArray("\xc3\x45\xae"); - th_comment_init(&d->comment); - th_info_init(&d->info); - d->setup_info = 0; - d->ctx = 0; -} - -QXmppTheoraEncoder::~QXmppTheoraEncoder() -{ - th_comment_clear(&d->comment); - th_info_clear(&d->info); - if (d->setup_info) - th_setup_free(d->setup_info); - if (d->ctx) - th_encode_free(d->ctx); - delete d; -} - -bool QXmppTheoraEncoder::setFormat(const QXmppVideoFormat &format) -{ - const QXmppVideoFrame::PixelFormat pixelFormat = format.pixelFormat(); - if ((pixelFormat != QXmppVideoFrame::Format_YUV420P) && - (pixelFormat != QXmppVideoFrame::Format_YUYV)) { - qWarning("Theora encoder does not support the given format"); - return false; - } - - d->info.frame_width = format.frameSize().width(); - d->info.frame_height = format.frameSize().height(); - d->info.pic_height = format.frameSize().height(); - d->info.pic_width = format.frameSize().width(); - d->info.pic_x = 0; - d->info.pic_y = 0; - d->info.colorspace = TH_CS_UNSPECIFIED; - d->info.target_bitrate = 0; - d->info.quality = 48; - d->info.keyframe_granule_shift = 6; - - // FIXME: how do we handle floating point frame rates? - d->info.fps_numerator = format.frameRate(); - d->info.fps_denominator = 1; - - if (pixelFormat == QXmppVideoFrame::Format_YUV420P) { - d->info.pixel_fmt = TH_PF_420; - d->ycbcr_buffer[0].width = d->info.frame_width; - d->ycbcr_buffer[0].height = d->info.frame_height; - d->ycbcr_buffer[1].width = d->ycbcr_buffer[0].width / 2; - d->ycbcr_buffer[1].height = d->ycbcr_buffer[0].height / 2; - d->ycbcr_buffer[2].width = d->ycbcr_buffer[1].width; - d->ycbcr_buffer[2].height = d->ycbcr_buffer[1].height; - } else if (pixelFormat == QXmppVideoFrame::Format_YUYV) { - d->info.pixel_fmt = TH_PF_422; - d->buffer.resize(d->info.frame_width * d->info.frame_height * 2); - d->ycbcr_buffer[0].width = d->info.frame_width; - d->ycbcr_buffer[0].height = d->info.frame_height; - d->ycbcr_buffer[0].stride = d->info.frame_width; - d->ycbcr_buffer[0].data = (uchar *)d->buffer.data(); - d->ycbcr_buffer[1].width = d->ycbcr_buffer[0].width / 2; - d->ycbcr_buffer[1].height = d->ycbcr_buffer[0].height; - d->ycbcr_buffer[1].stride = d->ycbcr_buffer[0].stride / 2; - d->ycbcr_buffer[1].data = d->ycbcr_buffer[0].data + d->ycbcr_buffer[0].stride * d->ycbcr_buffer[0].height; - d->ycbcr_buffer[2].width = d->ycbcr_buffer[1].width; - d->ycbcr_buffer[2].height = d->ycbcr_buffer[1].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; - } - - // create encoder - if (d->ctx) { - th_encode_free(d->ctx); - d->ctx = 0; - } - d->ctx = th_encode_alloc(&d->info); - if (!d->ctx) { - qWarning("Theora encoder could not be allocated"); - return false; - } - - // fetch headers - QList headers; - ogg_packet packet; - while (th_encode_flushheader(d->ctx, &d->comment, &packet) > 0) - headers << QByteArray((const char *)packet.packet, packet.bytes); - - // store configuration - d->configuration.clear(); - QDataStream stream(&d->configuration, QIODevice::WriteOnly); - stream << quint32(1); - - quint16 length = 0; - for (const auto &header : headers) - length += header.size(); - - quint8 h_count = headers.size() - 1; - stream.writeRawData(d->ident.constData(), d->ident.size()); - stream << length; - stream << h_count; -#ifdef QXMPP_DEBUG_THEORA - qDebug("Theora packed header %u ident=%s bytes=%u count=%u", 0, d->ident.toHex().data(), length, h_count); -#endif - - // write header sizes - for (int h = 0; h < h_count; ++h) { - quint16 h_size = headers[h].size(); - do { - quint8 b = (h_size & 0x7f); - h_size >>= 7; - if (h_size) - b |= 0x80; - stream << b; - } while (h_size); - } - - // write headers - for (int h = 0; h < headers.size(); ++h) { -#ifdef QXMPP_DEBUG_THEORA - qDebug("Header %d size %d", h, headers[h].size()); -#endif - stream.writeRawData(headers[h].data(), headers[h].size()); - } - - return true; -} - -QList QXmppTheoraEncoder::handleFrame(const QXmppVideoFrame &frame) -{ - QList packets; - const int PACKET_MAX = 1388; - - if (!d->ctx) - return packets; - - if (d->info.pixel_fmt == TH_PF_420) { - d->ycbcr_buffer[0].stride = frame.bytesPerLine(); - d->ycbcr_buffer[0].data = (unsigned char *)frame.bits(); - d->ycbcr_buffer[1].stride = d->ycbcr_buffer[0].stride / 2; - 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) { - // YUV 4:2:2 unpacking - const int width = frame.width(); - const int height = frame.height(); - const int stride = frame.bytesPerLine(); - const uchar *row = frame.bits(); - uchar *y_out = d->ycbcr_buffer[0].data; - uchar *cb_out = d->ycbcr_buffer[1].data; - uchar *cr_out = d->ycbcr_buffer[2].data; - for (int y = 0; y < height; ++y) { - const uchar *ptr = row; - for (int x = 0; x < width; x += 2) { - *(y_out++) = *(ptr++); - *(cb_out++) = *(ptr++); - *(y_out++) = *(ptr++); - *(cr_out++) = *(ptr++); - } - row += stride; - } - } else { - qWarning("Theora encoder received an unsupported frame format"); - return packets; - } - - if (th_encode_ycbcr_in(d->ctx, d->ycbcr_buffer) != 0) { - qWarning("Theora encoder could not handle frame"); - return packets; - } - - QByteArray payload; - ogg_packet packet; - while (th_encode_packetout(d->ctx, 0, &packet) > 0) { -#ifdef QXMPP_DEBUG_THEORA - qDebug("Theora encoded packet %d bytes", packet.bytes); -#endif - QDataStream stream(&payload, QIODevice::WriteOnly); - const char *data = (const char *)packet.packet; - int size = packet.bytes; - if (size <= PACKET_MAX) { - // no fragmentation - stream.device()->reset(); - payload.resize(0); - d->writeFragment(stream, NoFragment, 1, 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, 0, data, length); - data += length; - size -= length; - frag_type = (size > PACKET_MAX) ? MiddleFragment : EndFragment; - packets << payload; - } - } - } - - return packets; -} - -QMap QXmppTheoraEncoder::parameters() const -{ - QMap params; - if (d->ctx) { - params.insert("delivery-method", "inline"); - params.insert("configuration", d->configuration.toBase64()); - } - return params; -} - -#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) -{ - // With the VPX_DL_REALTIME option, tries to decode the frame as quick as - // possible, if not possible discard it. - if (vpx_codec_decode(&codec, - (const uint8_t *)buffer.constData(), - buffer.size(), - NULL, - VPX_DL_REALTIME) != 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; - vpx_codec_flags_t flags = 0; - - // Enable FEC if codec support it. - if (vpx_codec_get_caps(vpx_codec_vp8_dx()) & VPX_CODEC_CAP_ERROR_CONCEALMENT) - flags |= VPX_CODEC_USE_ERROR_CONCEALMENT; - - if (vpx_codec_dec_init(&d->codec, - vpx_codec_vp8_dx(), - NULL, - flags) != 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 QXmppVpxDecoder::handlePacket(const QXmppRtpPacket &packet) -{ - QList frames; - const QByteArray payload = packet.payload(); - - // vp8 deframing: http://tools.ietf.org/html/draft-westin-payload-vp8-00 - QDataStream stream(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 = payload.size() - 1; -#ifdef QXMPP_DEBUG_VPX - qDebug("Vpx fragment FI: %d, size %d", frag_type, packetLength); -#endif - - QXmppVideoFrame frame; - static quint16 sequence = 0; - - // If the incoming packet sequence is wrong discard all packets until a - // complete keyframe arrives. - // If a partition of a keyframe is missing, discard it until a next - // keyframe. - // - // NOTE: https://tools.ietf.org/html/draft-ietf-payload-vp8-13#section-4.3 - // Sections: 4.3, 4.5, 4.5.1 - - if (frag_type == NoFragment) { - // unfragmented packet - if ((payload[1] & 0x1) == 0 // is key frame - || packet.sequence() == sequence) { - if (d->decodeFrame(payload.mid(1), &frame)) - frames << frame; - - sequence = packet.sequence() + 1; - } - - d->packetBuffer.resize(0); - } else { - // fragments - if (frag_type == StartFragment) { - // start fragment - if ((payload[1] & 0x1) == 0 // is key frame - || packet.sequence() == sequence) { - d->packetBuffer = payload.mid(1); - sequence = packet.sequence() + 1; - } - } else { - // continuation or end fragment - if (packet.sequence() == sequence) { - 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); - } - } - - sequence++; - } - } - } - - return frames; -} - -bool QXmppVpxDecoder::setParameters(const QMap ¶meters) -{ - Q_UNUSED(parameters); - 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(uint clockrate) -{ - d = new QXmppVpxEncoderPrivate; - d->frameCount = 0; - d->imageBuffer = 0; - vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &d->cfg, 0); - - // Set the encoding threads number to use - int nThreads = QThread::idealThreadCount(); - - if (nThreads > 0) - d->cfg.g_threads = nThreads - 1; - - // Make stream error resiliant - d->cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS; - - d->cfg.g_pass = VPX_RC_ONE_PASS; - d->cfg.kf_mode = VPX_KF_AUTO; - - // Reduce GOP size - if (d->cfg.kf_max_dist > GOPSIZE) - d->cfg.kf_max_dist = GOPSIZE; - - // Here, clockrate is synonym of bitrate. - d->cfg.rc_target_bitrate = clockrate / 1000; -} - -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.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 QXmppVpxEncoder::handleFrame(const QXmppVideoFrame &frame) -{ - const int PACKET_MAX = 1388; - QList 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 QXmppVpxEncoder::parameters() const -{ - return QMap(); -} - -#endif diff --git a/src/base/QXmppCodec_p.h b/src/base/QXmppCodec_p.h deleted file mode 100644 index d9a8e3c2..00000000 --- a/src/base/QXmppCodec_p.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2008-2020 The QXmpp developers - * - * Author: - * Jeremy Lainé - * - * Source: - * https://github.com/qxmpp-project/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. - * - */ - -#ifndef QXMPPCODEC_H -#define QXMPPCODEC_H - -#include "QXmppGlobal.h" - -#include - -class QXmppRtpPacket; -class QXmppVideoFormat; -class QXmppVideoFrame; - -/// \brief The QXmppCodec class is the base class for audio codecs capable of -/// encoding and decoding audio samples. -/// -/// Samples must be 16-bit little endian. - -class QXMPP_AUTOTEST_EXPORT QXmppCodec -{ -public: - virtual ~QXmppCodec(); - - /// Reads samples from the input stream, encodes them and writes the - /// encoded data to the output stream. - virtual qint64 encode(QDataStream &input, QDataStream &output) = 0; - - /// Reads encoded data from the input stream, decodes it and writes the - /// decoded samples to the output stream. - virtual qint64 decode(QDataStream &input, QDataStream &output) = 0; -}; - -/// \internal -/// -/// The QXmppG711aCodec class represent a G.711 a-law PCM codec. - -class QXmppG711aCodec : public QXmppCodec -{ -public: - QXmppG711aCodec(int clockrate); - - qint64 encode(QDataStream &input, QDataStream &output) override; - qint64 decode(QDataStream &input, QDataStream &output) override; - -private: - int m_frequency; -}; - -/// \internal -/// -/// The QXmppG711uCodec class represent a G.711 u-law PCM codec. - -class QXmppG711uCodec : public QXmppCodec -{ -public: - QXmppG711uCodec(int clockrate); - - qint64 encode(QDataStream &input, QDataStream &output) override; - qint64 decode(QDataStream &input, QDataStream &output) override; - -private: - int m_frequency; -}; - -#ifdef QXMPP_USE_SPEEX -typedef struct SpeexBits SpeexBits; - -/// \internal -/// -/// The QXmppSpeexCodec class represent a SPEEX codec. - -class QXMPP_AUTOTEST_EXPORT QXmppSpeexCodec : public QXmppCodec -{ -public: - QXmppSpeexCodec(int clockrate); - ~QXmppSpeexCodec(); - - qint64 encode(QDataStream &input, QDataStream &output); - qint64 decode(QDataStream &input, QDataStream &output); - -private: - SpeexBits *encoder_bits; - void *encoder_state; - SpeexBits *decoder_bits; - void *decoder_state; - int frame_samples; -}; -#endif - -#ifdef QXMPP_USE_OPUS -typedef struct OpusEncoder OpusEncoder; -typedef struct OpusDecoder OpusDecoder; - -/// \internal -/// -/// The QXmppOpusCodec class represent a Opus codec. - -class QXMPP_AUTOTEST_EXPORT QXmppOpusCodec : public QXmppCodec -{ -public: - QXmppOpusCodec(int clockrate, int channels); - ~QXmppOpusCodec(); - - qint64 encode(QDataStream &input, QDataStream &output); - qint64 decode(QDataStream &input, QDataStream &output); - -private: - OpusEncoder *encoder; - OpusDecoder *decoder; - int sampleRate; - int nChannels; - QList validFrameSize; - int nSamples; - QByteArray sampleBuffer; - - int readWindow(int bufferSize); -}; -#endif - -/// \brief The QXmppVideoDecoder class is the base class for video decoders. -/// - -class QXMPP_AUTOTEST_EXPORT QXmppVideoDecoder -{ -public: - virtual ~QXmppVideoDecoder(); - - /// Returns the format of the video stream. - virtual QXmppVideoFormat format() const = 0; - - /// Handles an RTP \a packet and returns a list of decoded video frames. - virtual QList handlePacket(const QXmppRtpPacket &packet) = 0; - - /// Sets the video stream's \a parameters. - virtual bool setParameters(const QMap ¶meters) = 0; -}; - -/// \brief The QXmppVideoEncoder class is the base class for video encoders. -/// - -class QXMPP_AUTOTEST_EXPORT QXmppVideoEncoder -{ -public: - virtual ~QXmppVideoEncoder(); - - /// Sets the \a format of the video stream. - virtual bool setFormat(const QXmppVideoFormat &format) = 0; - - /// Handles a video \a frame and returns a list of RTP packet payloads. - virtual QList handleFrame(const QXmppVideoFrame &frame) = 0; - - /// Returns the video stream's parameters. - virtual QMap parameters() const = 0; -}; - -#ifdef QXMPP_USE_THEORA -class QXmppTheoraDecoderPrivate; -class QXmppTheoraEncoderPrivate; - -class QXMPP_AUTOTEST_EXPORT QXmppTheoraDecoder : public QXmppVideoDecoder -{ -public: - QXmppTheoraDecoder(); - ~QXmppTheoraDecoder(); - - QXmppVideoFormat format() const; - QList handlePacket(const QXmppRtpPacket &packet); - bool setParameters(const QMap ¶meters); - -private: - QXmppTheoraDecoderPrivate *d; -}; - -class QXMPP_AUTOTEST_EXPORT QXmppTheoraEncoder : public QXmppVideoEncoder -{ -public: - QXmppTheoraEncoder(); - ~QXmppTheoraEncoder(); - - bool setFormat(const QXmppVideoFormat &format); - QList handleFrame(const QXmppVideoFrame &frame); - QMap parameters() const; - -private: - QXmppTheoraEncoderPrivate *d; -}; -#endif - -#ifdef QXMPP_USE_VPX -class QXmppVpxDecoderPrivate; -class QXmppVpxEncoderPrivate; - -class QXMPP_AUTOTEST_EXPORT QXmppVpxDecoder : public QXmppVideoDecoder -{ -public: - QXmppVpxDecoder(); - ~QXmppVpxDecoder(); - - QXmppVideoFormat format() const; - QList handlePacket(const QXmppRtpPacket &packet); - bool setParameters(const QMap ¶meters); - -private: - QXmppVpxDecoderPrivate *d; -}; - -class QXMPP_AUTOTEST_EXPORT QXmppVpxEncoder : public QXmppVideoEncoder -{ -public: - QXmppVpxEncoder(uint clockrate = 0); - ~QXmppVpxEncoder(); - - bool setFormat(const QXmppVideoFormat &format); - QList handleFrame(const QXmppVideoFrame &frame); - QMap parameters() const; - -private: - QXmppVpxEncoderPrivate *d; -}; -#endif - -#endif diff --git a/src/base/QXmppRtcpPacket.cpp b/src/base/QXmppRtcpPacket.cpp deleted file mode 100644 index 5ddb96f0..00000000 --- a/src/base/QXmppRtcpPacket.cpp +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright (C) 2008-2020 The QXmpp developers - * - * Author: - * Jeremy Lainé - * - * Source: - * https://github.com/qxmpp-project/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 "QXmppRtcpPacket.h" - -#include -#include - -#define RTP_VERSION 2 - -enum DescriptionType { - CnameType = 1, - NameType = 2 -}; - -class QXmppRtcpPacketPrivate : public QSharedData -{ -public: - QXmppRtcpPacketPrivate(); - - /// Number of report blocks. - quint8 count; - /// Payload type. - quint8 type; - /// Raw payload data. - QByteArray payload; - - QString goodbyeReason; - QList goodbyeSsrcs; - QXmppRtcpSenderInfo senderInfo; - QList receiverReports; - QList sourceDescriptions; - quint32 ssrc; -}; - -class QXmppRtcpReceiverReportPrivate : public QSharedData -{ -public: - QXmppRtcpReceiverReportPrivate(); - bool read(QDataStream &stream); - void write(QDataStream &stream) const; - - quint32 ssrc; - quint8 fractionLost; - quint32 totalLost; - quint32 highestSequence; - quint32 jitter; - quint32 lsr; - quint32 dlsr; -}; - -class QXmppRtcpSenderInfoPrivate : public QSharedData -{ -public: - QXmppRtcpSenderInfoPrivate(); - bool read(QDataStream &stream); - void write(QDataStream &stream) const; - - quint64 ntpStamp; - quint32 rtpStamp; - quint32 packetCount; - quint32 octetCount; -}; - -class QXmppRtcpSourceDescriptionPrivate : public QSharedData -{ -public: - QXmppRtcpSourceDescriptionPrivate(); - bool read(QDataStream &stream); - void write(QDataStream &stream) const; - - quint32 ssrc; - QString cname; - QString name; -}; - -static bool readPadding(QDataStream &stream, int dataLength) -{ - if (dataLength % 4) { - QByteArray buffer; - buffer.resize(4 - dataLength % 4); - if (stream.readRawData(buffer.data(), buffer.size()) != buffer.size()) - return false; - if (buffer != QByteArray(buffer.size(), '\0')) - return false; - } - return true; -} - -static void writePadding(QDataStream &stream, int dataLength) -{ - if (dataLength % 4) { - const QByteArray buffer = QByteArray(4 - dataLength % 4, '\0'); - stream.writeRawData(buffer.constData(), buffer.size()); - } -} - -/// Constructs an empty RTCP packet - -QXmppRtcpPacket::QXmppRtcpPacket() - : d(new QXmppRtcpPacketPrivate()) -{ -} - -/// Constructs a copy of other. -/// -/// \param other - -QXmppRtcpPacket::QXmppRtcpPacket(const QXmppRtcpPacket &other) - : d(other.d) -{ -} - -QXmppRtcpPacket::~QXmppRtcpPacket() -{ -} - -/// Parses an RTCP packet. -/// -/// \param ba -bool QXmppRtcpPacket::decode(const QByteArray &ba) -{ - QDataStream stream(ba); - return read(stream); -} - -/// Encodes an RTCP packet. - -QByteArray QXmppRtcpPacket::encode() const -{ - QByteArray ba; - ba.resize(4 + d->payload.size()); - - QDataStream stream(&ba, QIODevice::WriteOnly); - write(stream); - return ba; -} - -bool QXmppRtcpPacket::read(QDataStream &stream) -{ - quint8 tmp, type; - quint16 len; - - // fixed header - stream >> tmp; - stream >> type; - stream >> len; - if (stream.status() != QDataStream::Ok) - return false; - - // check version - if ((tmp >> 6) != RTP_VERSION) - return false; - - const int payloadLength = len << 2; - d->count = (tmp & 0x1f); - d->type = type; - d->payload.resize(payloadLength); - if (stream.readRawData(d->payload.data(), payloadLength) != payloadLength) - return false; - - QDataStream s(d->payload); - d->goodbyeReason.clear(); - d->goodbyeSsrcs.clear(); - d->receiverReports.clear(); - d->senderInfo = QXmppRtcpSenderInfo(); - d->sourceDescriptions.clear(); - d->ssrc = 0; - if (d->type == Goodbye) { - quint32 ssrc; - for (int i = 0; i < d->count; ++i) { - s >> ssrc; - if (stream.status() != QDataStream::Ok) - return false; - d->goodbyeSsrcs << ssrc; - } - quint8 reasonLength; - s >> reasonLength; - if (reasonLength) { - QByteArray buffer; - buffer.resize(reasonLength); - if (s.readRawData(buffer.data(), buffer.size()) != buffer.size()) - return false; - if (!readPadding(s, 1 + buffer.size())) - return false; - d->goodbyeReason = QString::fromUtf8(buffer); - } - } else if (d->type == ReceiverReport || d->type == SenderReport) { - s >> d->ssrc; - if (d->type == SenderReport && !d->senderInfo.d->read(s)) - return false; - for (int i = 0; i < d->count; ++i) { - QXmppRtcpReceiverReport receiverReport; - if (!receiverReport.d->read(s)) - return false; - d->receiverReports << receiverReport; - } - } else if (d->type == SourceDescription) { - for (int i = 0; i < d->count; ++i) { - QXmppRtcpSourceDescription desc; - if (!desc.d->read(s)) - return false; - d->sourceDescriptions << desc; - } - } - return true; -} - -void QXmppRtcpPacket::write(QDataStream &stream) const -{ - QByteArray payload; - quint8 count; - - QDataStream s(&payload, QIODevice::WriteOnly); - if (d->type == Goodbye) { - count = d->goodbyeSsrcs.size(); - for (const auto ssrc : d->goodbyeSsrcs) - s << ssrc; - if (!d->goodbyeReason.isEmpty()) { - const QByteArray reason = d->goodbyeReason.toUtf8(); - s << quint8(reason.size()); - s.writeRawData(reason.constData(), reason.size()); - writePadding(s, 1 + reason.size()); - } - } else if (d->type == ReceiverReport || d->type == SenderReport) { - count = d->receiverReports.size(); - s << d->ssrc; - if (d->type == SenderReport) - d->senderInfo.d->write(s); - for (const auto &report : d->receiverReports) - report.d->write(s); - } else if (d->type == SourceDescription) { - count = d->sourceDescriptions.size(); - for (const auto &desc : d->sourceDescriptions) - desc.d->write(s); - } else { - count = d->count; - payload = d->payload; - } - - stream << quint8((RTP_VERSION << 6) | (count & 0x1f)); - stream << d->type; - stream << quint16(payload.size() >> 2); - stream.writeRawData(payload.constData(), payload.size()); -} - -QString QXmppRtcpPacket::goodbyeReason() const -{ - return d->goodbyeReason; -} - -void QXmppRtcpPacket::setGoodbyeReason(const QString &goodbyeReason) -{ - d->goodbyeReason = goodbyeReason; -} - -QList QXmppRtcpPacket::goodbyeSsrcs() const -{ - return d->goodbyeSsrcs; -} - -void QXmppRtcpPacket::setGoodbyeSsrcs(const QList &goodbyeSsrcs) -{ - d->goodbyeSsrcs = goodbyeSsrcs; -} - -QList QXmppRtcpPacket::receiverReports() const -{ - return d->receiverReports; -} - -void QXmppRtcpPacket::setReceiverReports(const QList &reports) -{ - d->receiverReports = reports; -} - -QXmppRtcpSenderInfo QXmppRtcpPacket::senderInfo() const -{ - return d->senderInfo; -} - -void QXmppRtcpPacket::setSenderInfo(const QXmppRtcpSenderInfo &senderInfo) -{ - d->senderInfo = senderInfo; -} - -QList QXmppRtcpPacket::sourceDescriptions() const -{ - return d->sourceDescriptions; -} - -void QXmppRtcpPacket::setSourceDescriptions(const QList &descriptions) -{ - d->sourceDescriptions = descriptions; -} - -/// Returns the RTCP packet's source SSRC. -/// -/// This is only applicable for Sender Reports or Receiver Reports. - -quint32 QXmppRtcpPacket::ssrc() const -{ - return d->ssrc; -} - -/// Sets the RTCP packet's source SSRC. -/// -/// This is only applicable for Sender Reports or Receiver Reports. - -void QXmppRtcpPacket::setSsrc(quint32 ssrc) -{ - d->ssrc = ssrc; -} - -/// Returns the RTCP packet type. - -quint8 QXmppRtcpPacket::type() const -{ - return d->type; -} - -/// Sets the RTCP packet type. -/// -/// \param type - -void QXmppRtcpPacket::setType(quint8 type) -{ - d->type = type; -} - -QXmppRtcpPacketPrivate::QXmppRtcpPacketPrivate() - : count(0), type(0), ssrc(0) -{ -} - -/// Constructs an empty receiver report. - -QXmppRtcpReceiverReport::QXmppRtcpReceiverReport() - : d(new QXmppRtcpReceiverReportPrivate()) -{ -} - -/// Constructs a copy of other. -/// -/// \param other - -QXmppRtcpReceiverReport::QXmppRtcpReceiverReport(const QXmppRtcpReceiverReport &other) - : d(other.d) -{ -} - -QXmppRtcpReceiverReport::~QXmppRtcpReceiverReport() -{ -} - -quint32 QXmppRtcpReceiverReport::dlsr() const -{ - return d->dlsr; -} - -void QXmppRtcpReceiverReport::setDlsr(quint32 dlsr) -{ - d->dlsr = dlsr; -} - -quint8 QXmppRtcpReceiverReport::fractionLost() const -{ - return d->fractionLost; -} - -void QXmppRtcpReceiverReport::setFractionLost(quint8 fractionLost) -{ - d->fractionLost = fractionLost; -} - -quint32 QXmppRtcpReceiverReport::jitter() const -{ - return d->jitter; -} - -void QXmppRtcpReceiverReport::setJitter(quint32 jitter) -{ - d->jitter = jitter; -} - -quint32 QXmppRtcpReceiverReport::lsr() const -{ - return d->lsr; -} - -void QXmppRtcpReceiverReport::setLsr(quint32 lsr) -{ - d->lsr = lsr; -} - -quint32 QXmppRtcpReceiverReport::ssrc() const -{ - return d->ssrc; -} - -void QXmppRtcpReceiverReport::setSsrc(quint32 ssrc) -{ - d->ssrc = ssrc; -} - -quint32 QXmppRtcpReceiverReport::totalLost() const -{ - return d->totalLost; -} - -void QXmppRtcpReceiverReport::setTotalLost(quint32 totalLost) -{ - d->totalLost = totalLost; -} - -QXmppRtcpReceiverReportPrivate::QXmppRtcpReceiverReportPrivate() - : ssrc(0), fractionLost(0), totalLost(0), highestSequence(0), jitter(0), lsr(0), dlsr(0) -{ -} - -bool QXmppRtcpReceiverReportPrivate::read(QDataStream &stream) -{ - quint32 tmp; - stream >> ssrc; - stream >> tmp; - fractionLost = (tmp >> 24) & 0xff; - totalLost = tmp & 0xffffff; - stream >> highestSequence; - stream >> jitter; - stream >> lsr; - stream >> dlsr; - return stream.status() == QDataStream::Ok; -} - -void QXmppRtcpReceiverReportPrivate::write(QDataStream &stream) const -{ - stream << ssrc; - stream << quint32((fractionLost << 24) | (totalLost & 0xffffff)); - stream << highestSequence; - stream << jitter; - stream << lsr; - stream << dlsr; -} - -/// Constructs an empty sender report. - -QXmppRtcpSenderInfo::QXmppRtcpSenderInfo() - : d(new QXmppRtcpSenderInfoPrivate()) -{ -} - -/// Constructs a copy of other. -/// -/// \param other - -QXmppRtcpSenderInfo::QXmppRtcpSenderInfo(const QXmppRtcpSenderInfo &other) - : d(other.d) -{ -} - -QXmppRtcpSenderInfo::~QXmppRtcpSenderInfo() -{ -} - -quint64 QXmppRtcpSenderInfo::ntpStamp() const -{ - return d->ntpStamp; -} - -void QXmppRtcpSenderInfo::setNtpStamp(quint64 ntpStamp) -{ - d->ntpStamp = ntpStamp; -} - -quint32 QXmppRtcpSenderInfo::rtpStamp() const -{ - return d->rtpStamp; -} - -void QXmppRtcpSenderInfo::setRtpStamp(quint32 rtpStamp) -{ - d->rtpStamp = rtpStamp; -} - -quint32 QXmppRtcpSenderInfo::octetCount() const -{ - return d->octetCount; -} - -void QXmppRtcpSenderInfo::setOctetCount(quint32 count) -{ - d->octetCount = count; -} - -quint32 QXmppRtcpSenderInfo::packetCount() const -{ - return d->packetCount; -} - -void QXmppRtcpSenderInfo::setPacketCount(quint32 count) -{ - d->packetCount = count; -} - -QXmppRtcpSenderInfoPrivate::QXmppRtcpSenderInfoPrivate() - : ntpStamp(0), rtpStamp(0), packetCount(0), octetCount(0) -{ -} - -bool QXmppRtcpSenderInfoPrivate::read(QDataStream &stream) -{ - stream >> ntpStamp; - stream >> rtpStamp; - stream >> packetCount; - stream >> octetCount; - return stream.status() == QDataStream::Ok; -} - -void QXmppRtcpSenderInfoPrivate::write(QDataStream &stream) const -{ - stream << ntpStamp; - stream << rtpStamp; - stream << packetCount; - stream << octetCount; -} - -/// Constructs an empty source description - -QXmppRtcpSourceDescription::QXmppRtcpSourceDescription() - : d(new QXmppRtcpSourceDescriptionPrivate()) -{ -} - -/// Constructs a copy of other. -/// -/// \param other - -QXmppRtcpSourceDescription::QXmppRtcpSourceDescription(const QXmppRtcpSourceDescription &other) - : d(other.d) -{ -} - -QXmppRtcpSourceDescription::~QXmppRtcpSourceDescription() -{ -} - -QString QXmppRtcpSourceDescription::cname() const -{ - return d->cname; -} - -void QXmppRtcpSourceDescription::setCname(const QString &cname) -{ - d->cname = cname; -} - -QString QXmppRtcpSourceDescription::name() const -{ - return d->name; -} - -void QXmppRtcpSourceDescription::setName(const QString &name) -{ - d->name = name; -} - -quint32 QXmppRtcpSourceDescription::ssrc() const -{ - return d->ssrc; -} - -void QXmppRtcpSourceDescription::setSsrc(quint32 ssrc) -{ - d->ssrc = ssrc; -} - -QXmppRtcpSourceDescriptionPrivate::QXmppRtcpSourceDescriptionPrivate() - : ssrc(0) -{ -} - -bool QXmppRtcpSourceDescriptionPrivate::read(QDataStream &stream) -{ - QByteArray buffer; - quint8 itemType, itemLength; - quint16 chunkLength = 0; - - stream >> ssrc; - if (stream.status() != QDataStream::Ok) - return false; - while (true) { - stream >> itemType; - if (stream.status() != QDataStream::Ok) - return false; - if (!itemType) { - chunkLength++; - break; - } - - stream >> itemLength; - if (stream.status() != QDataStream::Ok) - return false; - - buffer.resize(itemLength); - if (stream.readRawData(buffer.data(), itemLength) != itemLength) - return false; - chunkLength += itemLength + 2; - - if (itemType == CnameType) - cname = QString::fromUtf8(buffer); - else if (itemType == NameType) - name = QString::fromUtf8(buffer); - } - return readPadding(stream, chunkLength); -} - -void QXmppRtcpSourceDescriptionPrivate::write(QDataStream &stream) const -{ - QByteArray buffer; - quint16 chunkLength = 0; - - stream << ssrc; - if (!cname.isEmpty()) { - buffer = cname.toUtf8(); - stream << quint8(CnameType); - stream << quint8(buffer.size()); - stream.writeRawData(buffer.constData(), buffer.size()); - chunkLength += 2 + buffer.size(); - } - if (!name.isEmpty()) { - buffer = name.toUtf8(); - stream << quint8(NameType); - stream << quint8(buffer.size()); - stream.writeRawData(buffer.constData(), buffer.size()); - chunkLength += 2 + buffer.size(); - } - stream << quint8(0); - chunkLength++; - writePadding(stream, chunkLength); -} diff --git a/src/base/QXmppRtcpPacket.h b/src/base/QXmppRtcpPacket.h deleted file mode 100644 index a2041672..00000000 --- a/src/base/QXmppRtcpPacket.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2008-2020 The QXmpp developers - * - * Author: - * Jeremy Lainé - * - * Source: - * https://github.com/qxmpp-project/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. - * - */ - -#ifndef QXMPPRTCPPACKET_H -#define QXMPPRTCPPACKET_H - -#include "QXmppGlobal.h" - -#include - -class QXmppRtcpPacketPrivate; -class QXmppRtcpReceiverReport; -class QXmppRtcpReceiverReportPrivate; -class QXmppRtcpSenderInfo; -class QXmppRtcpSenderInfoPrivate; -class QXmppRtcpSourceDescription; -class QXmppRtcpSourceDescriptionPrivate; - -/// \internal -/// -/// The QXmppRtcpPacket class represents an RTCP packet. - -class QXMPP_EXPORT QXmppRtcpPacket -{ -public: - enum Type { - SenderReport = 200, - ReceiverReport = 201, - SourceDescription = 202, - Goodbye = 203, - }; - - QXmppRtcpPacket(); - QXmppRtcpPacket(const QXmppRtcpPacket &other); - ~QXmppRtcpPacket(); - - bool decode(const QByteArray &ba); - QByteArray encode() const; - - bool read(QDataStream &stream); - void write(QDataStream &stream) const; - - QString goodbyeReason() const; - void setGoodbyeReason(const QString &goodbyeReason); - - QList goodbyeSsrcs() const; - void setGoodbyeSsrcs(const QList &goodbyeSsrcs); - - QList receiverReports() const; - void setReceiverReports(const QList &reports); - - QXmppRtcpSenderInfo senderInfo() const; - void setSenderInfo(const QXmppRtcpSenderInfo &senderInfo); - - QList sourceDescriptions() const; - void setSourceDescriptions(const QList &descriptions); - - quint32 ssrc() const; - void setSsrc(quint32 ssrc); - - quint8 type() const; - void setType(quint8 type); - -private: - QSharedDataPointer d; -}; - -/// \internal - -class QXMPP_EXPORT QXmppRtcpReceiverReport -{ -public: - QXmppRtcpReceiverReport(); - QXmppRtcpReceiverReport(const QXmppRtcpReceiverReport &other); - ~QXmppRtcpReceiverReport(); - - quint32 dlsr() const; - void setDlsr(quint32 dlsr); - - quint8 fractionLost() const; - void setFractionLost(quint8 fractionLost); - - quint32 jitter() const; - void setJitter(quint32 jitter); - - quint32 lsr() const; - void setLsr(quint32 lsr); - - quint32 ssrc() const; - void setSsrc(quint32 ssrc); - - quint32 totalLost() const; - void setTotalLost(quint32 totalLost); - -private: - friend class QXmppRtcpPacket; - QSharedDataPointer d; -}; - -/// \internal - -class QXMPP_EXPORT QXmppRtcpSenderInfo -{ -public: - QXmppRtcpSenderInfo(); - QXmppRtcpSenderInfo(const QXmppRtcpSenderInfo &other); - ~QXmppRtcpSenderInfo(); - - quint64 ntpStamp() const; - void setNtpStamp(quint64 ntpStamp); - - quint32 rtpStamp() const; - void setRtpStamp(quint32 rtpStamp); - - quint32 octetCount() const; - void setOctetCount(quint32 count); - - quint32 packetCount() const; - void setPacketCount(quint32 count); - -private: - friend class QXmppRtcpPacket; - QSharedDataPointer d; -}; - -/// \internal - -class QXMPP_EXPORT QXmppRtcpSourceDescription -{ -public: - QXmppRtcpSourceDescription(); - QXmppRtcpSourceDescription(const QXmppRtcpSourceDescription &other); - ~QXmppRtcpSourceDescription(); - - QString cname() const; - void setCname(const QString &name); - - QString name() const; - void setName(const QString &name); - - quint32 ssrc() const; - void setSsrc(const quint32 ssrc); - -private: - friend class QXmppRtcpPacket; - QSharedDataPointer d; -}; - -#endif diff --git a/src/base/QXmppRtpChannel.cpp b/src/base/QXmppRtpChannel.cpp deleted file mode 100644 index 848cd3d3..00000000 --- a/src/base/QXmppRtpChannel.cpp +++ /dev/null @@ -1,999 +0,0 @@ -/* - * Copyright (C) 2008-2020 The QXmpp developers - * - * Author: - * Jeremy Lainé - * - * Source: - * https://github.com/qxmpp-project/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 "QXmppRtpChannel.h" - -#include "QXmppCodec_p.h" -#include "QXmppJingleIq.h" -#include "QXmppRtpPacket.h" - -#include - -#include -#include -#include - -#ifndef M_PI -#define M_PI 3.14159265358979323846264338327950288 -#endif - -//#define QXMPP_DEBUG_RTP -//#define QXMPP_DEBUG_RTP_BUFFER -#define SAMPLE_BYTES 2 - -/// Creates a new RTP channel. - -QXmppRtpChannel::QXmppRtpChannel() - : m_outgoingPayloadNumbered(false) -{ - m_outgoingSsrc = qrand(); -} - -/// Returns the local payload types. -/// - -QList QXmppRtpChannel::localPayloadTypes() -{ - m_outgoingPayloadNumbered = true; - return m_outgoingPayloadTypes; -} - -/// Sets the remote payload types. -/// -/// \param remotePayloadTypes - -void QXmppRtpChannel::setRemotePayloadTypes(const QList &remotePayloadTypes) -{ - QList commonOutgoingTypes; - QList commonIncomingTypes; - - for (const auto &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 negotiate a common codec"); - return; - } - m_incomingPayloadTypes = commonIncomingTypes; - m_outgoingPayloadTypes = commonOutgoingTypes; - m_outgoingPayloadNumbered = true; - - // call hook - payloadTypesChanged(); -} - -/// Returns the local SSRC. - -quint32 QXmppRtpChannel::localSsrc() const -{ - return m_outgoingSsrc; -} - -/// Sets the local SSRC. -/// -/// \param ssrc - -void QXmppRtpChannel::setLocalSsrc(quint32 ssrc) -{ - m_outgoingSsrc = ssrc; -} - -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 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 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(); - QXmppCodec *codecForPayloadType(const QXmppJinglePayloadType &payloadType); - - // signals - bool signalsEmitted; - qint64 writtenSinceLastEmit; - - // RTP - QHostAddress remoteHost; - quint16 remotePort; - - QByteArray incomingBuffer; - bool incomingBuffering; - QMap 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 outgoingTones; - QXmppJinglePayloadType outgoingTonesType; - - QXmppJinglePayloadType payloadType; -}; - -QXmppRtpAudioChannelPrivate::QXmppRtpAudioChannelPrivate() - : signalsEmitted(false), writtenSinceLastEmit(0), incomingBuffering(true), incomingMinimum(0), incomingMaximum(0), incomingPos(0), incomingSequence(0), outgoingCodec(nullptr), outgoingMarker(true), outgoingPayloadNumbered(false), outgoingSequence(1), outgoingStamp(0), outgoingTimer(nullptr) -{ - qRegisterMetaType("QXmppRtpAudioChannel::Tone"); -} - -/// 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 -#ifdef QXMPP_USE_OPUS - else if (payloadType.name().toLower() == "opus") - return new QXmppOpusCodec(payloadType.clockrate(), payloadType.channels()); -#endif - return nullptr; -} - -/// Constructs a new RTP audio channel with the given \a parent. - -QXmppRtpAudioChannel::QXmppRtpAudioChannel(QObject *parent) - : QIODevice(parent), d(new QXmppRtpAudioChannelPrivate()) -{ - auto *logParent = qobject_cast(parent); - if (logParent) { - connect(this, &QXmppRtpAudioChannel::logMessage, - logParent, &QXmppLoggable::logMessage); - } - d->outgoingTimer = new QTimer(this); - connect(d->outgoingTimer, &QTimer::timeout, this, &QXmppRtpAudioChannel::writeDatagram); - - // set supported codecs - QXmppJinglePayloadType payload; - -#ifdef QXMPP_USE_OPUS - payload.setId(100); // NOTE: I don't know if this Id is ok for Opus. - payload.setChannels(1); - payload.setName("opus"); - payload.setClockrate(8000); - m_outgoingPayloadTypes << payload; -#endif - -#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 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() -{ - qDeleteAll(d->incomingCodecs); - - if (d->outgoingCodec) - delete d->outgoingCodec; - delete d; -} - -/// Returns the number of bytes that are available for reading. - -qint64 QXmppRtpAudioChannel::bytesAvailable() const -{ - return QIODevice::bytesAvailable() + d->incomingBuffer.size(); -} - -/// Closes the RTP audio 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 = nullptr; - const quint8 packetType = packet.type(); - if (!d->incomingCodecs.contains(packetType)) { - for (const auto &payload : m_incomingPayloadTypes) { - if (packetType == payload.id()) { - codec = d->codecForPayloadType(payload); - break; - } - } - if (codec) - d->incomingCodecs.insert(packetType, codec); - else - warning(QString("Could not find codec for RTP type %1").arg(QString::number(packetType))); - } else { - codec = d->incomingCodecs.value(packetType); - } - 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! - const 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; -} - -/// Returns the mode in which the channel has been opened. - -QIODevice::OpenMode QXmppRtpAudioChannel::openMode() const -{ - return QIODevice::openMode(); -} - -/// 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; -} - -/// \cond -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; -} - -void QXmppRtpAudioChannel::payloadTypesChanged() -{ - // delete incoming codecs - qDeleteAll(d->incomingCodecs); - - // delete outgoing codec - if (d->outgoingCodec) { - delete d->outgoingCodec; - d->outgoingCodec = nullptr; - } - - // create outgoing codec - for (const auto &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); -} -/// \endcond - -/// 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 (auto &outgoingTone : d->outgoingTones) { - if (outgoingTone.tone == tone) { - outgoingTone.finished = true; - break; - } - } -} - -/// \cond -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; -} -/// \endcond - -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.setMarker(info.outgoingStart == d->outgoingStamp); - packet.setType(d->outgoingTonesType.id()); - packet.setSequence(d->outgoingSequence); - packet.setStamp(info.outgoingStart); - packet.setSsrc(localSsrc()); - - QByteArray payload; - QDataStream output(&payload, QIODevice::WriteOnly); - output << quint8(info.tone); - output << quint8(info.finished ? 0x80 : 0x00); - output << quint16(d->outgoingStamp + packetTicks - info.outgoingStart); - packet.setPayload(payload); -#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; - if (d->outgoingMarker) { - packet.setMarker(true); - d->outgoingMarker = false; - } else { - packet.setMarker(false); - } - packet.setType(d->payloadType.id()); - packet.setSequence(d->outgoingSequence); - packet.setStamp(d->outgoingStamp); - packet.setSsrc(localSsrc()); - - // encode audio chunk - QDataStream input(chunk); - input.setByteOrder(QDataStream::LittleEndian); - QByteArray payload; - QDataStream output(&payload, QIODevice::WriteOnly); - const qint64 packetTicks = d->outgoingCodec->encode(input, output); - packet.setPayload(payload); - -#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); -} - -/// Returns a pointer to the start of the frame data buffer. - -uchar *QXmppVideoFrame::bits() -{ - return (uchar *)m_data.data(); -} - -/// Returns a pointer to the start of the frame data buffer. - -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 decoders; - QXmppVideoEncoder *encoder; - QList frames; - - // local - QXmppVideoFormat outgoingFormat; - quint8 outgoingId; - quint16 outgoingSequence; - quint32 outgoingStamp; -}; - -QXmppRtpVideoChannelPrivate::QXmppRtpVideoChannelPrivate() - : encoder(nullptr), - outgoingId(0), - outgoingSequence(1), - outgoingStamp(0) -{ -} - -/// Constructs a new RTP video channel with the given \a parent. - -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(256000); - 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() -{ - qDeleteAll(d->decoders); - if (d->encoder) - delete d->encoder; - delete d; -} - -/// Closes the RTP video 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); -} - -/// Returns the video format used by the encoder. - -QXmppVideoFormat QXmppRtpVideoChannel::decoderFormat() const -{ - if (d->decoders.isEmpty()) - return QXmppVideoFormat(); - const int key = d->decoders.keys().first(); - return d->decoders.value(key)->format(); -} - -/// Returns the video format used by the encoder. - -QXmppVideoFormat QXmppRtpVideoChannel::encoderFormat() const -{ - return d->outgoingFormat; -} - -/// Sets the video format used by the encoder. - -void QXmppRtpVideoChannel::setEncoderFormat(const QXmppVideoFormat &format) -{ - if (d->encoder && !d->encoder->setFormat(format)) - return; - d->outgoingFormat = format; -} - -/// Returns the mode in which the channel has been opened. - -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; -} - -/// \cond -void QXmppRtpVideoChannel::payloadTypesChanged() -{ - // refresh decoders - qDeleteAll(d->decoders); - d->decoders.clear(); - - for (const auto &payload : qAsConst(m_incomingPayloadTypes)) { - QXmppVideoDecoder *decoder = nullptr; - 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 = nullptr; - } - for (const auto &payload : m_outgoingPayloadTypes) { - QXmppVideoEncoder *encoder = nullptr; - 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(payload.clockrate()); - } -#endif - if (encoder) { - encoder->setFormat(d->outgoingFormat); - d->encoder = encoder; - d->outgoingId = payload.id(); - break; - } - } -} -/// \endcond - -/// Decodes buffered RTP packets and returns a list of video frames. - -QList QXmppRtpVideoChannel::readFrames() -{ - const QList frames = d->frames; - d->frames.clear(); - return frames; -} - -/// Encodes a video \a frame and sends RTP packets. - -void QXmppRtpVideoChannel::writeFrame(const QXmppVideoFrame &frame) -{ - if (!d->encoder) { - warning("QXmppRtpVideoChannel::writeFrame before codec was set"); - return; - } - - QXmppRtpPacket packet; - packet.setMarker(false); - packet.setType(d->outgoingId); - packet.setSsrc(localSsrc()); - for (const auto &payload : d->encoder->handleFrame(frame)) { - packet.setSequence(d->outgoingSequence++); - packet.setStamp(d->outgoingStamp); - packet.setPayload(payload); -#ifdef QXMPP_DEBUG_RTP - logSent(packet.toString()); -#endif - emit sendDatagram(packet.encode()); - } - d->outgoingStamp += 1; -} diff --git a/src/base/QXmppRtpChannel.h b/src/base/QXmppRtpChannel.h deleted file mode 100644 index cc934a59..00000000 --- a/src/base/QXmppRtpChannel.h +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2008-2020 The QXmpp developers - * - * Author: - * Jeremy Lainé - * - * Source: - * https://github.com/qxmpp-project/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. - * - */ - -#ifndef QXMPPRTPCHANNEL_H -#define QXMPPRTPCHANNEL_H - -#include "QXmppJingleIq.h" -#include "QXmppLogger.h" - -#include -#include - -class QXmppCodec; -class QXmppJinglePayloadType; -class QXmppRtpAudioChannelPrivate; -class QXmppRtpVideoChannelPrivate; - -class QXMPP_EXPORT QXmppRtpChannel -{ -public: - QXmppRtpChannel(); - - /// Closes the RTP channel. - virtual void close() = 0; - - /// Returns the mode in which the channel has been opened. - virtual QIODevice::OpenMode openMode() const = 0; - - QList localPayloadTypes(); - void setRemotePayloadTypes(const QList &remotePayloadTypes); - - quint32 localSsrc() const; - void setLocalSsrc(quint32 ssrc); - -protected: - /// \cond - virtual void payloadTypesChanged() = 0; - - QList m_incomingPayloadTypes; - QList m_outgoingPayloadTypes; - bool m_outgoingPayloadNumbered; - /// \endcond - -private: - quint32 m_outgoingSsrc; -}; - -/// \brief The QXmppRtpAudioChannel class represents an RTP audio channel to a remote party. -/// -/// It acts as a QIODevice so that you can read / write audio samples, for -/// instance using a QAudioOutput and a QAudioInput. -/// -/// \note THIS API IS NOT FINALIZED YET - -class QXMPP_EXPORT QXmppRtpAudioChannel : public QIODevice, public QXmppRtpChannel -{ - Q_OBJECT - -public: - /// This enum is used to describe a DTMF tone. - enum Tone { - Tone_0 = 0, ///< Tone for the 0 key. - Tone_1, ///< Tone for the 1 key. - Tone_2, ///< Tone for the 2 key. - Tone_3, ///< Tone for the 3 key. - Tone_4, ///< Tone for the 4 key. - Tone_5, ///< Tone for the 5 key. - Tone_6, ///< Tone for the 6 key. - Tone_7, ///< Tone for the 7 key. - Tone_8, ///< Tone for the 8 key. - Tone_9, ///< Tone for the 9 key. - Tone_Star, ///< Tone for the * key. - Tone_Pound, ///< Tone for the # key. - Tone_A, ///< Tone for the A key. - Tone_B, ///< Tone for the B key. - Tone_C, ///< Tone for the C key. - Tone_D ///< Tone for the D key. - }; - Q_ENUM(Tone) - - QXmppRtpAudioChannel(QObject *parent = nullptr); - ~QXmppRtpAudioChannel() override; - - qint64 bytesAvailable() const override; - void close() override; - bool isSequential() const override; - QIODevice::OpenMode openMode() const override; - QXmppJinglePayloadType payloadType() const; - qint64 pos() const override; - bool seek(qint64 pos) override; - -Q_SIGNALS: - /// \brief This signal is emitted when a datagram needs to be sent. - void sendDatagram(const QByteArray &ba); - - /// \brief This signal is emitted to send logging messages. - void logMessage(QXmppLogger::MessageType type, const QString &msg); - -public Q_SLOTS: - void datagramReceived(const QByteArray &ba); - void startTone(QXmppRtpAudioChannel::Tone tone); - void stopTone(QXmppRtpAudioChannel::Tone tone); - -protected: - /// \cond - void debug(const QString &message) - { - emit logMessage(QXmppLogger::DebugMessage, qxmpp_loggable_trace(message)); - } - - void warning(const QString &message) - { - emit logMessage(QXmppLogger::WarningMessage, qxmpp_loggable_trace(message)); - } - - void logReceived(const QString &message) - { - emit logMessage(QXmppLogger::ReceivedMessage, qxmpp_loggable_trace(message)); - } - - void logSent(const QString &message) - { - emit logMessage(QXmppLogger::SentMessage, qxmpp_loggable_trace(message)); - } - - void payloadTypesChanged() override; - qint64 readData(char *data, qint64 maxSize) override; - qint64 writeData(const char *data, qint64 maxSize) override; - /// \endcond - -private Q_SLOTS: - void emitSignals(); - void writeDatagram(); - -private: - friend class QXmppRtpAudioChannelPrivate; - QXmppRtpAudioChannelPrivate *d; -}; - -/// \brief The QXmppVideoFrame class provides a representation of a frame of video data. -/// -/// \note THIS API IS NOT FINALIZED YET - -class QXMPP_EXPORT QXmppVideoFrame -{ -public: - /// This enum describes a pixel format. - enum PixelFormat { - Format_Invalid = 0, ///< The frame is invalid. - Format_RGB32 = 3, ///< The frame stored using a 32-bit RGB format (0xffRRGGBB). - Format_RGB24 = 4, ///< The frame is stored using a 24-bit RGB format (8-8-8). - Format_YUV420P = 18, ///< The frame is stored using an 8-bit per component planar - ///< YUV format with the U and V planes horizontally and - ///< vertically sub-sampled, i.e. the height and width of the - ///< U and V planes are half that of the Y plane. - Format_UYVY = 20, ///< The frame is stored using an 8-bit per component packed - ///< YUV format with the U and V planes horizontally - ///< sub-sampled (U-Y-V-Y), i.e. two horizontally adjacent - ///< pixels are stored as a 32-bit macropixel which has a Y - ///< value for each pixel and common U and V values. - Format_YUYV = 21 ///< The frame is stored using an 8-bit per component packed - ///< YUV format with the U and V planes horizontally - ///< sub-sampled (Y-U-Y-V), i.e. two horizontally adjacent - ///< pixels are stored as a 32-bit macropixel which has a Y - ///< value for each pixel and common U and V values. - }; - - QXmppVideoFrame(); - QXmppVideoFrame(int bytes, const QSize &size, int bytesPerLine, PixelFormat format); - uchar *bits(); - const uchar *bits() const; - int bytesPerLine() const; - int height() const; - bool isValid() const; - int mappedBytes() const; - PixelFormat pixelFormat() const; - QSize size() const; - int width() const; - -private: - int m_bytesPerLine; - QByteArray m_data; - int m_height; - int m_mappedBytes; - PixelFormat m_pixelFormat; - int m_width; -}; - -class QXMPP_EXPORT QXmppVideoFormat -{ -public: - QXmppVideoFormat() - : m_frameRate(15.0), m_frameSize(QSize(320, 240)), m_pixelFormat(QXmppVideoFrame::Format_YUYV) - { - } - - int frameHeight() const - { - return m_frameSize.height(); - } - - int frameWidth() const - { - return m_frameSize.width(); - } - - qreal frameRate() const - { - return m_frameRate; - } - - void setFrameRate(qreal frameRate) - { - m_frameRate = frameRate; - } - - QSize frameSize() const - { - return m_frameSize; - } - - void setFrameSize(const QSize &frameSize) - { - m_frameSize = frameSize; - } - - QXmppVideoFrame::PixelFormat pixelFormat() const - { - return m_pixelFormat; - } - - void setPixelFormat(QXmppVideoFrame::PixelFormat pixelFormat) - { - m_pixelFormat = pixelFormat; - } - -private: - qreal m_frameRate; - QSize m_frameSize; - QXmppVideoFrame::PixelFormat m_pixelFormat; -}; - -/// \brief The QXmppRtpVideoChannel class represents an RTP video channel to a remote party. -/// -/// \note THIS API IS NOT FINALIZED YET - -class QXMPP_EXPORT QXmppRtpVideoChannel : public QXmppLoggable, public QXmppRtpChannel -{ - Q_OBJECT - -public: - QXmppRtpVideoChannel(QObject *parent = nullptr); - ~QXmppRtpVideoChannel() override; - - void close() override; - QIODevice::OpenMode openMode() const override; - - // incoming stream - QXmppVideoFormat decoderFormat() const; - QList readFrames(); - - // outgoing stream - QXmppVideoFormat encoderFormat() const; - void setEncoderFormat(const QXmppVideoFormat &format); - void writeFrame(const QXmppVideoFrame &frame); - -Q_SIGNALS: - /// \brief This signal is emitted when a datagram needs to be sent. - void sendDatagram(const QByteArray &ba); - -public Q_SLOTS: - void datagramReceived(const QByteArray &ba); - -protected: - /// \cond - void payloadTypesChanged() override; - /// \endcond - -private: - friend class QXmppRtpVideoChannelPrivate; - QXmppRtpVideoChannelPrivate *d; -}; - -#endif diff --git a/src/base/QXmppRtpPacket.cpp b/src/base/QXmppRtpPacket.cpp deleted file mode 100644 index 0a467a7f..00000000 --- a/src/base/QXmppRtpPacket.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2008-2020 The QXmpp developers - * - * Author: - * Jeremy Lainé - * - * Source: - * https://github.com/qxmpp-project/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 "QXmppRtpPacket.h" - -#include -#include - -#define RTP_VERSION 2 - -class QXmppRtpPacketPrivate : public QSharedData -{ -public: - QXmppRtpPacketPrivate(); - - /// Marker flag. - bool marker; - /// Payload type. - quint8 type; - /// Synchronization source. - quint32 ssrc; - /// Contributing sources. - QList csrc; - /// Sequence number. - quint16 sequence; - /// Timestamp. - quint32 stamp; - /// Raw payload data. - QByteArray payload; -}; - -QXmppRtpPacketPrivate::QXmppRtpPacketPrivate() - : marker(false), type(0), ssrc(0), sequence(0), stamp(0) -{ -} - -/// Constructs an empty RTP packet - -QXmppRtpPacket::QXmppRtpPacket() - : d(new QXmppRtpPacketPrivate()) -{ -} - -/// Constructs a copy of other. -/// -/// \param other -/// -QXmppRtpPacket::QXmppRtpPacket(const QXmppRtpPacket &other) - : d(other.d) -{ -} - -QXmppRtpPacket::~QXmppRtpPacket() -{ -} - -/// Assigns the other packet to this one. -/// -/// \param other -/// -QXmppRtpPacket &QXmppRtpPacket::operator=(const QXmppRtpPacket &other) -{ - d = other.d; - return *this; -} - -/// 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; - const quint8 cc = (tmp & 0xf); - const int hlen = 12 + 4 * cc; - if ((tmp >> 6) != RTP_VERSION || ba.size() < hlen) - return false; - stream >> tmp; - d->marker = (tmp >> 7); - d->type = tmp & 0x7f; - stream >> d->sequence; - stream >> d->stamp; - stream >> d->ssrc; - - // contributing source IDs - d->csrc.clear(); - quint32 src; - for (int i = 0; i < cc; ++i) { - stream >> src; - d->csrc << src; - } - - // retrieve payload - d->payload = ba.right(ba.size() - hlen); - return true; -} - -/// Encodes an RTP packet. - -QByteArray QXmppRtpPacket::encode() const -{ - Q_ASSERT(d->csrc.size() < 16); - - // fixed header - QByteArray ba; - ba.resize(d->payload.size() + 12 + 4 * d->csrc.size()); - QDataStream stream(&ba, QIODevice::WriteOnly); - stream << quint8((RTP_VERSION << 6) | - (d->csrc.size() & 0xf)); - stream << quint8((d->type & 0x7f) | (d->marker << 7)); - stream << d->sequence; - stream << d->stamp; - stream << d->ssrc; - - // contributing source ids - for (const auto &src : d->csrc) - stream << src; - - stream.writeRawData(d->payload.constData(), d->payload.size()); - return ba; -} - -QList QXmppRtpPacket::csrc() const -{ - return d->csrc; -} - -void QXmppRtpPacket::setCsrc(const QList &csrc) -{ - d->csrc = csrc; -} - -bool QXmppRtpPacket::marker() const -{ - return d->marker; -} - -void QXmppRtpPacket::setMarker(bool marker) -{ - d->marker = marker; -} - -QByteArray QXmppRtpPacket::payload() const -{ - return d->payload; -} - -void QXmppRtpPacket::setPayload(const QByteArray &payload) -{ - d->payload = payload; -} - -quint32 QXmppRtpPacket::ssrc() const -{ - return d->ssrc; -} - -void QXmppRtpPacket::setSsrc(quint32 ssrc) -{ - d->ssrc = ssrc; -} - -quint16 QXmppRtpPacket::sequence() const -{ - return d->sequence; -} - -void QXmppRtpPacket::setSequence(quint16 sequence) -{ - d->sequence = sequence; -} - -quint32 QXmppRtpPacket::stamp() const -{ - return d->stamp; -} - -void QXmppRtpPacket::setStamp(quint32 stamp) -{ - d->stamp = stamp; -} - -quint8 QXmppRtpPacket::type() const -{ - return d->type; -} - -void QXmppRtpPacket::setType(quint8 type) -{ - d->type = type; -} - -/// 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(d->sequence), QString::number(d->stamp), QString::number(d->marker), QString::number(d->type), QString::number(d->payload.size())); -} diff --git a/src/base/QXmppRtpPacket.h b/src/base/QXmppRtpPacket.h deleted file mode 100644 index dd479eeb..00000000 --- a/src/base/QXmppRtpPacket.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2008-2020 The QXmpp developers - * - * Author: - * Jeremy Lainé - * - * Source: - * https://github.com/qxmpp-project/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. - * - */ - -#ifndef QXMPPRTPPACKET_H -#define QXMPPRTPPACKET_H - -#include "QXmppGlobal.h" - -#include - -class QXmppRtpPacketPrivate; - -/// \internal -/// -/// The QXmppRtpPacket class represents an RTP packet. - -class QXMPP_EXPORT QXmppRtpPacket -{ -public: - QXmppRtpPacket(); - QXmppRtpPacket(const QXmppRtpPacket &other); - ~QXmppRtpPacket(); - - QXmppRtpPacket &operator=(const QXmppRtpPacket &other); - - bool decode(const QByteArray &ba); - QByteArray encode() const; - QString toString() const; - - QList csrc() const; - void setCsrc(const QList &csrc); - - bool marker() const; - void setMarker(bool marker); - - QByteArray payload() const; - void setPayload(const QByteArray &payload); - - quint16 sequence() const; - void setSequence(quint16 sequence); - - quint32 ssrc() const; - void setSsrc(quint32 ssrc); - - quint32 stamp() const; - void setStamp(quint32 stamp); - - quint8 type() const; - void setType(quint8 type); - -private: - QSharedDataPointer d; -}; - -#endif -- cgit v1.2.3