kopia lustrzana https://github.com/f4exb/sdrangel
Copy to UDP/RTP: implemented G722
rodzic
752e71c402
commit
202d67f141
|
@ -15,6 +15,8 @@
|
|||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "audionetsink.h"
|
||||
#include "util/rtpsink.h"
|
||||
|
||||
|
@ -33,7 +35,7 @@ AudioNetSink::AudioNetSink(QObject *parent) :
|
|||
m_bufferIndex(0),
|
||||
m_port(9998)
|
||||
{
|
||||
memset(m_data, 0, 65536);
|
||||
std::fill(m_data, m_data+m_dataBlockSize, 0);
|
||||
m_udpSocket = new QUdpSocket(parent);
|
||||
}
|
||||
|
||||
|
@ -47,7 +49,7 @@ AudioNetSink::AudioNetSink(QObject *parent, int sampleRate, bool stereo) :
|
|||
m_bufferIndex(0),
|
||||
m_port(9998)
|
||||
{
|
||||
memset(m_data, 0, 65536);
|
||||
std::fill(m_data, m_data+m_dataBlockSize, 0);
|
||||
m_udpSocket = new QUdpSocket(parent);
|
||||
m_rtpBufferAudio = new RTPSink(m_udpSocket, sampleRate, stereo);
|
||||
}
|
||||
|
@ -130,6 +132,9 @@ void AudioNetSink::setParameters(Codec codec, bool stereo, int sampleRate)
|
|||
case CodecL8:
|
||||
m_rtpBufferAudio->setPayloadInformation(RTPSink::PayloadL8, sampleRate);
|
||||
break;
|
||||
case CodecG722:
|
||||
m_rtpBufferAudio->setPayloadInformation(RTPSink::PayloadG722, sampleRate/2);
|
||||
break;
|
||||
case CodecL16: // actually no codec
|
||||
default:
|
||||
m_rtpBufferAudio->setPayloadInformation(stereo ? RTPSink::PayloadL16Stereo : RTPSink::PayloadL16Mono, sampleRate);
|
||||
|
@ -173,35 +178,44 @@ void AudioNetSink::write(qint16 isample)
|
|||
m_udpSocket->writeDatagram((const char*)m_data, (qint64 ) m_udpBlockSize, m_address, m_port);
|
||||
m_bufferIndex = 0;
|
||||
}
|
||||
else
|
||||
|
||||
switch(m_codec)
|
||||
{
|
||||
switch(m_codec)
|
||||
{
|
||||
case CodecPCMA:
|
||||
case CodecPCMU:
|
||||
{
|
||||
qint8 *p = (qint8*) &m_data[m_bufferIndex];
|
||||
*p = m_audioCompressor.compress8(sample);
|
||||
m_bufferIndex += sizeof(qint8);
|
||||
}
|
||||
break;
|
||||
case CodecL8:
|
||||
{
|
||||
qint8 *p = (qint8*) &m_data[m_bufferIndex];
|
||||
*p = sample / 256;
|
||||
m_bufferIndex += sizeof(qint8);
|
||||
}
|
||||
break;
|
||||
case CodecL16:
|
||||
default:
|
||||
{
|
||||
qint16 *p = (qint16*) &m_data[m_bufferIndex];
|
||||
*p = sample;
|
||||
m_bufferIndex += sizeof(qint16);
|
||||
}
|
||||
break;
|
||||
case CodecPCMA:
|
||||
case CodecPCMU:
|
||||
{
|
||||
qint8 *p = (qint8*) &m_data[m_bufferIndex];
|
||||
*p = m_audioCompressor.compress8(sample);
|
||||
m_bufferIndex += sizeof(qint8);
|
||||
}
|
||||
break;
|
||||
case CodecL8:
|
||||
{
|
||||
qint8 *p = (qint8*) &m_data[m_bufferIndex];
|
||||
*p = sample / 256;
|
||||
m_bufferIndex += sizeof(qint8);
|
||||
}
|
||||
break;
|
||||
case CodecG722:
|
||||
{
|
||||
qint16 *p = (qint16*) &m_data[m_udpBlockSize + 2*m_bufferIndex];
|
||||
*p = sample;
|
||||
m_bufferIndex += 1;
|
||||
|
||||
if (m_bufferIndex == m_udpBlockSize) {
|
||||
m_g722.encode((uint8_t *) m_data, (const int16_t*) &m_data[3*m_udpBlockSize], m_udpBlockSize);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CodecL16:
|
||||
default:
|
||||
{
|
||||
qint16 *p = (qint16*) &m_data[m_bufferIndex];
|
||||
*p = sample;
|
||||
m_bufferIndex += sizeof(qint16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (m_type == SinkRTP)
|
||||
{
|
||||
|
@ -220,6 +234,22 @@ void AudioNetSink::write(qint16 isample)
|
|||
m_rtpBufferAudio->write((uint8_t *) &p);
|
||||
}
|
||||
break;
|
||||
case CodecG722:
|
||||
{
|
||||
if (m_bufferIndex >= m_g722BlockSize)
|
||||
{
|
||||
static const int sz = m_g722BlockSize / sizeof(int16_t);
|
||||
uint8_t g722_data[sz];
|
||||
m_g722.encode(g722_data, (const int16_t*) m_data, sz);
|
||||
m_rtpBufferAudio->write(g722_data, sz);
|
||||
m_bufferIndex = 0;
|
||||
}
|
||||
|
||||
qint16 *p = (qint16*) &m_data[m_bufferIndex];
|
||||
*p = sample;
|
||||
m_bufferIndex += sizeof(qint16);
|
||||
}
|
||||
break;
|
||||
case CodecL16:
|
||||
default:
|
||||
m_rtpBufferAudio->write((uint8_t *) &sample);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "dsp/dsptypes.h"
|
||||
#include "audiofilter.h"
|
||||
#include "audiocompressor.h"
|
||||
#include "audiog722.h"
|
||||
#include "export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
@ -44,7 +45,8 @@ public:
|
|||
CodecL16, //!< Linear 16 bit samples (no formatting)
|
||||
CodecL8, //!< Linear 8 bit samples
|
||||
CodecPCMA, //!< PCM A-law 8 bit samples
|
||||
CodecPCMU //!< PCM Mu-law 8 bit samples
|
||||
CodecPCMU, //!< PCM Mu-law 8 bit samples
|
||||
CodecG722 //!< G722 compressed 8 bit samples 16kS/s in 8kS/s out
|
||||
} Codec;
|
||||
|
||||
AudioNetSink(QObject *parent); //!< without RTP
|
||||
|
@ -66,6 +68,8 @@ public:
|
|||
void moveToThread(QThread *thread);
|
||||
|
||||
static const int m_udpBlockSize;
|
||||
static const int m_dataBlockSize = 65536;
|
||||
static const int m_g722BlockSize = 1024; // size in bytes
|
||||
|
||||
protected:
|
||||
SinkType m_type;
|
||||
|
@ -73,11 +77,12 @@ protected:
|
|||
QUdpSocket *m_udpSocket;
|
||||
RTPSink *m_rtpBufferAudio;
|
||||
AudioCompressor m_audioCompressor;
|
||||
AudioG722 m_g722;
|
||||
AudioFilter m_audioFilter;
|
||||
int m_sampleRate;
|
||||
uint32_t m_decimation;
|
||||
uint32_t m_decimationCount;
|
||||
char m_data[65536];
|
||||
char m_data[m_dataBlockSize];
|
||||
unsigned int m_bufferIndex;
|
||||
QHostAddress m_address;
|
||||
unsigned int m_port;
|
||||
|
|
|
@ -46,7 +46,8 @@ public:
|
|||
UDPCodecL16, //!< Linear 16 bit (no codec)
|
||||
UDPCodecL8, //!< Linear 8 bit
|
||||
UDPCodecALaw, //!< PCM A-law 8 bit
|
||||
UDPCodecULaw //!< PCM Mu-law 8 bit
|
||||
UDPCodecULaw, //!< PCM Mu-law 8 bit
|
||||
UDPCodecG722 //!< G722 compression
|
||||
};
|
||||
|
||||
AudioOutput();
|
||||
|
|
|
@ -104,6 +104,12 @@ void RTPSink::setPayloadInformation(PayloadType payloadType, int sampleRate)
|
|||
m_packetSamples = m_sampleRate / 50; // 20ms packet samples
|
||||
timestampinc = m_sampleRate / 100; // 2 channels
|
||||
break;
|
||||
case PayloadG722:
|
||||
m_sampleBytes = 1;
|
||||
m_rtpSession.SetDefaultPayloadType(9);
|
||||
m_packetSamples = m_sampleRate / 50; // 20ms packet samples
|
||||
timestampinc = m_sampleRate / 50; // 1 channel
|
||||
break;
|
||||
case PayloadL16Mono:
|
||||
default:
|
||||
m_sampleBytes = 2;
|
||||
|
@ -226,10 +232,10 @@ void RTPSink::write(const uint8_t *sampleByte)
|
|||
qCritical("RTPSink::write: cannot write packet: %s", qrtplib::RTPGetErrorString(status).c_str());
|
||||
}
|
||||
|
||||
writeNetBuf(&m_byteBuffer[0],
|
||||
sampleByte,
|
||||
elemLength(m_payloadType),
|
||||
m_sampleBytes,
|
||||
writeNetBuf(&m_byteBuffer[0],
|
||||
sampleByte,
|
||||
elemLength(m_payloadType),
|
||||
m_sampleBytes,
|
||||
m_endianReverse);
|
||||
m_sampleBufferIndex = 1;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,8 @@ public:
|
|||
PayloadL16Stereo,
|
||||
PayloadL8,
|
||||
PayloadPCMA8,
|
||||
PayloadPCMU8
|
||||
PayloadPCMU8,
|
||||
PayloadG722
|
||||
} PayloadType;
|
||||
|
||||
RTPSink(QUdpSocket *udpSocket, int sampleRate, bool stereo);
|
||||
|
|
|
@ -1,10 +1,29 @@
|
|||
#include "audiodialog.h"
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2015-2019 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// This program is free software; you can redistribute it and/or modify //
|
||||
// it under the terms of the GNU General Public License as published by //
|
||||
// the Free Software Foundation as version 3 of the License, or //
|
||||
// //
|
||||
// This program 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 General Public License V3 for more details. //
|
||||
// //
|
||||
// You should have received a copy of the GNU General Public License //
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <audio/audiodevicemanager.h>
|
||||
#include <QTreeWidgetItem>
|
||||
#include "ui_audiodialog.h"
|
||||
#include <math.h>
|
||||
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "audio/audiodevicemanager.h"
|
||||
#include "audiodialog.h"
|
||||
#include "ui_audiodialog.h"
|
||||
|
||||
AudioDialogX::AudioDialogX(AudioDeviceManager* audioDeviceManager, QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::AudioDialog),
|
||||
|
@ -263,26 +282,43 @@ void AudioDialogX::updateOutputDeviceInfo()
|
|||
void AudioDialogX::updateOutputSDPString()
|
||||
{
|
||||
QString format;
|
||||
|
||||
switch(m_outputDeviceInfo.udpChannelCodec)
|
||||
{
|
||||
case AudioOutput::UDPCodecALaw:
|
||||
format = "PCMA";
|
||||
break;
|
||||
case AudioOutput::UDPCodecULaw:
|
||||
format = "PCMU";
|
||||
break;
|
||||
case AudioOutput::UDPCodecL8:
|
||||
format = "L8";
|
||||
break;
|
||||
case AudioOutput::UDPCodecL16:
|
||||
default:
|
||||
format = "L16";
|
||||
break;
|
||||
}
|
||||
|
||||
int nChannels = m_outputDeviceInfo.udpChannelMode == AudioOutput::UDPChannelStereo ? 2 : 1;
|
||||
uint32_t decimationFactor = m_outputDeviceInfo.udpDecimationFactor == 0 ? 1 : m_outputDeviceInfo.udpDecimationFactor;
|
||||
|
||||
if (m_outputDeviceInfo.udpChannelCodec == AudioOutput::UDPCodecALaw)
|
||||
{
|
||||
format = "PCMA";
|
||||
if ((nChannels != 1) || (m_outputDeviceInfo.sampleRate/decimationFactor != 8000)) {
|
||||
QMessageBox::information(this, tr("Message"), tr("PCMA must be 8000 Hz single channel"));
|
||||
}
|
||||
}
|
||||
else if (m_outputDeviceInfo.udpChannelCodec == AudioOutput::UDPCodecULaw)
|
||||
{
|
||||
format = "PCMU";
|
||||
if ((nChannels != 1) || (m_outputDeviceInfo.sampleRate/decimationFactor != 8000)) {
|
||||
QMessageBox::information(this, tr("Message"), tr("PCMU must be 8000 Hz single channel"));
|
||||
}
|
||||
}
|
||||
else if (m_outputDeviceInfo.udpChannelCodec == AudioOutput::UDPCodecG722)
|
||||
{
|
||||
format = "G722";
|
||||
if ((nChannels != 1) || (m_outputDeviceInfo.sampleRate/decimationFactor != 16000)) {
|
||||
QMessageBox::information(this, tr("Message"), tr("G722 must be 16000 Hz single channel"));
|
||||
}
|
||||
}
|
||||
else if (m_outputDeviceInfo.udpChannelCodec == AudioOutput::UDPCodecL8)
|
||||
{
|
||||
format = "L8";
|
||||
}
|
||||
else if (m_outputDeviceInfo.udpChannelCodec == AudioOutput::UDPCodecL16)
|
||||
{
|
||||
format = "L16";
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::information(this, tr("Message"), tr("Unknown codec"));
|
||||
format = "UNK";
|
||||
}
|
||||
|
||||
ui->outputSDPText->setText(tr("%1/%2/%3").arg(format).arg(m_outputDeviceInfo.sampleRate/decimationFactor).arg(nChannels));
|
||||
}
|
||||
|
|
|
@ -255,6 +255,11 @@
|
|||
<string>PCMU</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>G722</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
|
Ładowanie…
Reference in New Issue