mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-25 17:28:50 -05:00
Added PCMA and PCMU encoding for UDP/RTP audio
This commit is contained in:
parent
bfb686742a
commit
1de6ea4e60
@ -17,6 +17,11 @@
|
||||
|
||||
#include "audiocompressor.h"
|
||||
|
||||
const uint16_t AudioCompressor::ALAW_MAX = 0xFFF;
|
||||
const uint16_t AudioCompressor::MULAW_MAX = 0x1FFF;
|
||||
const uint16_t AudioCompressor::MULAW_BIAS = 33;
|
||||
|
||||
|
||||
AudioCompressor::AudioCompressor()
|
||||
{
|
||||
fillLUT2();
|
||||
@ -79,9 +84,117 @@ void AudioCompressor::fillLUT2()
|
||||
}
|
||||
}
|
||||
|
||||
void AudioCompressor::fillALaw()
|
||||
{
|
||||
for (int i=0; i<8*4096; i++) {
|
||||
m_lut[i] = ALaw_Encode(i/2 + 16384);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioCompressor::fillULaw()
|
||||
{
|
||||
for (int i=0; i<8*4096; i++) {
|
||||
m_lut[i] = MuLaw_Encode(i/2 + 16384);
|
||||
}
|
||||
}
|
||||
|
||||
int16_t AudioCompressor::compress(int16_t sample)
|
||||
{
|
||||
int16_t sign = sample < 0 ? -1 : 1;
|
||||
int16_t abs = sample < 0 ? -sample : sample;
|
||||
return sign * m_lut[abs];
|
||||
}
|
||||
|
||||
int8_t AudioCompressor::compress8(int16_t sample)
|
||||
{
|
||||
return ALaw_Encode(sample);
|
||||
//return m_lut[sample/2 + 16384];
|
||||
}
|
||||
|
||||
/* http://dystopiancode.blogspot.com/2012/02/pcm-law-and-u-law-companding-algorithms.html
|
||||
*
|
||||
* First, the number is verified is its negative. If the number is negative he will be made
|
||||
* positive and the sign variable (by default 0) will contain the value 0x80
|
||||
* (so when it's OR-ed to the coded result it will determine it's sign).
|
||||
*
|
||||
* Since the A-Law algorithm considers numbers in the range 0x000 - 0xFFF
|
||||
* (without considering the sign bit), if a number is bigger than 0xFFF, it will automatically
|
||||
* made equal to 0xFFF in order to avoid further problems.
|
||||
*
|
||||
* The first step in determining the coded value is finding the position of the first bit
|
||||
* who has a 1 value (excluding the sign bit). The search is started from position 11
|
||||
* and is continued until a bit with the value 1 is find or until a position smaller than 5 is met.
|
||||
*
|
||||
* If the position is smaller than 5 (there was no 1 bit found on the positions 11-5),
|
||||
* the least significant byte of the coded number is made equal the the bits 5,4,3,2
|
||||
* of the original number. Otherwise the least significant bit of the coded number is equal
|
||||
* to the first four bits who come after the first 1 bit encountered.
|
||||
*
|
||||
* In the end, the most significant byte of the coded number is computed according to the position
|
||||
* of the first 1 bit (if not such this was found, then the position is considered).
|
||||
*
|
||||
* Also, before returning the result, the even bits of the result will be complemented
|
||||
* (by XOR-ing with 0x55).
|
||||
*/
|
||||
int8_t AudioCompressor::ALaw_Encode(int16_t number)
|
||||
{
|
||||
uint16_t mask = 0x800;
|
||||
uint8_t sign = 0;
|
||||
uint8_t position = 11;
|
||||
uint8_t lsb = 0;
|
||||
|
||||
if (number < 0)
|
||||
{
|
||||
number = -number;
|
||||
sign = 0x80;
|
||||
}
|
||||
|
||||
if (number > ALAW_MAX) {
|
||||
number = ALAW_MAX;
|
||||
}
|
||||
|
||||
for (; ((number & mask) != mask && position >= 5); mask >>= 1, position--) {
|
||||
}
|
||||
|
||||
lsb = (number >> ((position == 4) ? (1) : (position - 4))) & 0x0f;
|
||||
|
||||
return (sign | ((position - 4) << 4) | lsb) ^ 0x55;
|
||||
}
|
||||
|
||||
/* http://dystopiancode.blogspot.com/2012/02/pcm-law-and-u-law-companding-algorithms.html
|
||||
*
|
||||
* The µ-Law compression algorithm is very similar to the A-Law compression algorithm.
|
||||
* The main difference is that the µ-Law uses 13 bits instead of 12 bits, so the position
|
||||
* variable will be initialized with 12 instead of 11. In order to make sure that there will be
|
||||
* no number without a 1 bit in the first 12-5 positions, a bias value is added to the number
|
||||
* (in this case 33). So, since there is no special case (numbers who do not have a 1 bit in
|
||||
* the first 12-5 positions), this makes the algorithm less complex by eliminating some condtions.
|
||||
*
|
||||
* Also in the end all bits are complemented, not just the even ones.
|
||||
*/
|
||||
int8_t AudioCompressor::MuLaw_Encode(int16_t number)
|
||||
{
|
||||
uint16_t mask = 0x1000;
|
||||
uint8_t sign = 0;
|
||||
uint8_t position = 12;
|
||||
uint8_t lsb = 0;
|
||||
|
||||
if (number < 0)
|
||||
{
|
||||
number = -number;
|
||||
sign = 0x80;
|
||||
}
|
||||
|
||||
number += MULAW_BIAS;
|
||||
|
||||
if (number > MULAW_MAX) {
|
||||
number = MULAW_MAX;
|
||||
}
|
||||
|
||||
for (; ((number & mask) != mask && position >= 5); mask >>= 1, position--) {
|
||||
}
|
||||
|
||||
lsb = (number >> (position - 4)) & 0x0f;
|
||||
|
||||
return (~(sign | ((position - 5) << 4) | lsb));
|
||||
}
|
||||
|
@ -28,10 +28,20 @@ public:
|
||||
~AudioCompressor();
|
||||
void fillLUT(); //!< 4 bands
|
||||
void fillLUT2(); //!< 8 bands (default)
|
||||
void fillALaw(); //!< A-law compression to 8 bits
|
||||
void fillULaw(); //!< u-law compression to 8 bits
|
||||
int16_t compress(int16_t sample);
|
||||
int8_t compress8(int16_t sample);
|
||||
|
||||
private:
|
||||
int8_t ALaw_Encode(int16_t number);
|
||||
int8_t MuLaw_Encode(int16_t number);
|
||||
|
||||
int16_t m_lut[32768];
|
||||
static const uint16_t ALAW_MAX;
|
||||
static const uint16_t MULAW_MAX;
|
||||
static const uint16_t MULAW_BIAS;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -42,15 +42,30 @@ QDataStream& operator>>(QDataStream& ds, AudioDeviceManager::InputDeviceInfo& in
|
||||
|
||||
QDataStream& operator<<(QDataStream& ds, const AudioDeviceManager::OutputDeviceInfo& info)
|
||||
{
|
||||
ds << info.sampleRate << info.udpAddress << info.udpPort << info.copyToUDP << info.udpUseRTP << (int) info.udpChannelMode;
|
||||
ds << info.sampleRate
|
||||
<< info.udpAddress
|
||||
<< info.udpPort
|
||||
<< info.copyToUDP
|
||||
<< info.udpUseRTP
|
||||
<< (int) info.udpChannelMode
|
||||
<< (int) info.udpChannelCodec;
|
||||
return ds;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& ds, AudioDeviceManager::OutputDeviceInfo& info)
|
||||
{
|
||||
int intChannelMode;
|
||||
ds >> info.sampleRate >> info.udpAddress >> info.udpPort >> info.copyToUDP >> info.udpUseRTP >> intChannelMode;
|
||||
int intChannelCodec;
|
||||
|
||||
ds >> info.sampleRate
|
||||
>> info.udpAddress
|
||||
>> info.udpPort
|
||||
>> info.copyToUDP
|
||||
>> info.udpUseRTP
|
||||
>> intChannelMode
|
||||
>> intChannelCodec;
|
||||
info.udpChannelMode = (AudioOutput::UDPChannelMode) intChannelMode;
|
||||
info.udpChannelCodec = (AudioOutput::UDPChannelCodec) intChannelCodec;
|
||||
return ds;
|
||||
}
|
||||
|
||||
@ -346,6 +361,7 @@ void AudioDeviceManager::startAudioOutput(int outputDeviceIndex)
|
||||
bool copyAudioToUDP;
|
||||
bool udpUseRTP;
|
||||
AudioOutput::UDPChannelMode udpChannelMode;
|
||||
AudioOutput::UDPChannelCodec udpChannelCodec;
|
||||
QString deviceName;
|
||||
|
||||
if (getOutputDeviceName(outputDeviceIndex, deviceName))
|
||||
@ -358,6 +374,7 @@ void AudioDeviceManager::startAudioOutput(int outputDeviceIndex)
|
||||
copyAudioToUDP = false;
|
||||
udpUseRTP = false;
|
||||
udpChannelMode = AudioOutput::UDPChannelLeft;
|
||||
udpChannelCodec = AudioOutput::UDPCodecL16;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -367,6 +384,7 @@ void AudioDeviceManager::startAudioOutput(int outputDeviceIndex)
|
||||
copyAudioToUDP = m_audioOutputInfos[deviceName].copyToUDP;
|
||||
udpUseRTP = m_audioOutputInfos[deviceName].udpUseRTP;
|
||||
udpChannelMode = m_audioOutputInfos[deviceName].udpChannelMode;
|
||||
udpChannelCodec = m_audioOutputInfos[deviceName].udpChannelCodec;
|
||||
}
|
||||
|
||||
m_audioOutputs[outputDeviceIndex]->start(outputDeviceIndex, sampleRate);
|
||||
@ -376,6 +394,7 @@ void AudioDeviceManager::startAudioOutput(int outputDeviceIndex)
|
||||
m_audioOutputInfos[deviceName].copyToUDP = copyAudioToUDP;
|
||||
m_audioOutputInfos[deviceName].udpUseRTP = udpUseRTP;
|
||||
m_audioOutputInfos[deviceName].udpChannelMode = udpChannelMode;
|
||||
m_audioOutputInfos[deviceName].udpChannelCodec = udpChannelCodec;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -588,7 +607,7 @@ void AudioDeviceManager::setOutputDeviceInfo(int outputDeviceIndex, const Output
|
||||
audioOutput->setUdpDestination(deviceInfo.udpAddress, deviceInfo.udpPort);
|
||||
audioOutput->setUdpUseRTP(deviceInfo.udpUseRTP);
|
||||
audioOutput->setUdpChannelMode(deviceInfo.udpChannelMode);
|
||||
audioOutput->setUdpChannelFormat(deviceInfo.udpChannelMode == AudioOutput::UDPChannelStereo, deviceInfo.sampleRate);
|
||||
audioOutput->setUdpChannelFormat(deviceInfo.udpChannelCodec, deviceInfo.udpChannelMode == AudioOutput::UDPChannelStereo, deviceInfo.sampleRate);
|
||||
|
||||
qDebug("AudioDeviceManager::setOutputDeviceInfo: index: %d device: %s updated",
|
||||
outputDeviceIndex, qPrintable(deviceName));
|
||||
@ -756,6 +775,7 @@ void AudioDeviceManager::debugAudioOutputInfos() const
|
||||
<< " udpPort: " << it.value().udpPort
|
||||
<< " copyToUDP: " << it.value().copyToUDP
|
||||
<< " udpUseRTP: " << it.value().udpUseRTP
|
||||
<< " udpChannelMode: " << (int) it.value().udpChannelMode;
|
||||
<< " udpChannelMode: " << (int) it.value().udpChannelMode
|
||||
<< " udpChannelCodec: " << (int) it.value().udpChannelCodec;
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ public:
|
||||
bool copyToUDP;
|
||||
bool udpUseRTP;
|
||||
AudioOutput::UDPChannelMode udpChannelMode;
|
||||
AudioOutput::UDPChannelCodec udpChannelCodec;
|
||||
friend QDataStream& operator<<(QDataStream& ds, const OutputDeviceInfo& info);
|
||||
friend QDataStream& operator>>(QDataStream& ds, OutputDeviceInfo& info);
|
||||
};
|
||||
|
@ -18,12 +18,14 @@
|
||||
#include "audionetsink.h"
|
||||
#include "util/rtpsink.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QUdpSocket>
|
||||
|
||||
const int AudioNetSink::m_udpBlockSize = 512;
|
||||
|
||||
AudioNetSink::AudioNetSink(QObject *parent) :
|
||||
m_type(SinkUDP),
|
||||
m_codec(CodecL16),
|
||||
m_rtpBufferAudio(0),
|
||||
m_bufferIndex(0),
|
||||
m_port(9998)
|
||||
@ -34,6 +36,7 @@ AudioNetSink::AudioNetSink(QObject *parent) :
|
||||
|
||||
AudioNetSink::AudioNetSink(QObject *parent, int sampleRate, bool stereo) :
|
||||
m_type(SinkUDP),
|
||||
m_codec(CodecL16),
|
||||
m_rtpBufferAudio(0),
|
||||
m_bufferIndex(0),
|
||||
m_port(9998)
|
||||
@ -95,10 +98,32 @@ void AudioNetSink::deleteDestination(const QString& address, uint16_t port)
|
||||
}
|
||||
}
|
||||
|
||||
void AudioNetSink::setParameters(bool stereo, int sampleRate)
|
||||
void AudioNetSink::setParameters(Codec codec, bool stereo, int sampleRate)
|
||||
{
|
||||
if (m_rtpBufferAudio) {
|
||||
m_rtpBufferAudio->setPayloadInformation(stereo ? RTPSink::PayloadL16Stereo : RTPSink::PayloadL16Mono, sampleRate);
|
||||
qDebug() << "AudioNetSink::setParameters:"
|
||||
<< " codec: " << codec
|
||||
<< " stereo: " << stereo
|
||||
<< " sampleRate: " << sampleRate;
|
||||
|
||||
m_codec = codec;
|
||||
|
||||
if (m_rtpBufferAudio)
|
||||
{
|
||||
switch (m_codec)
|
||||
{
|
||||
case CodecPCMA:
|
||||
m_audioCompressor.fillALaw();
|
||||
m_rtpBufferAudio->setPayloadInformation(RTPSink::PayloadPCMA8, sampleRate);
|
||||
break;
|
||||
case CodecPCMU:
|
||||
m_audioCompressor.fillULaw();
|
||||
m_rtpBufferAudio->setPayloadInformation(RTPSink::PayloadPCMU8, sampleRate);
|
||||
break;
|
||||
case CodecL16: // actually no codec
|
||||
default:
|
||||
m_rtpBufferAudio->setPayloadInformation(stereo ? RTPSink::PayloadL16Stereo : RTPSink::PayloadL16Mono, sampleRate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,14 +138,43 @@ void AudioNetSink::write(qint16 sample)
|
||||
}
|
||||
else
|
||||
{
|
||||
qint16 *p = (qint16*) &m_data[m_bufferIndex];
|
||||
*p = sample;
|
||||
m_bufferIndex += sizeof(qint16);
|
||||
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 CodecL16:
|
||||
default:
|
||||
{
|
||||
qint16 *p = (qint16*) &m_data[m_bufferIndex];
|
||||
*p = sample;
|
||||
m_bufferIndex += sizeof(qint16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_type == SinkRTP)
|
||||
{
|
||||
m_rtpBufferAudio->write((uint8_t *) &sample);
|
||||
switch(m_codec)
|
||||
{
|
||||
case CodecPCMA:
|
||||
case CodecPCMU:
|
||||
{
|
||||
qint8 p = m_audioCompressor.compress8(sample);
|
||||
m_rtpBufferAudio->write((uint8_t *) &p);
|
||||
}
|
||||
break;
|
||||
case CodecL16:
|
||||
default:
|
||||
m_rtpBufferAudio->write((uint8_t *) &sample);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,35 +203,35 @@ void AudioNetSink::write(qint16 lSample, qint16 rSample)
|
||||
}
|
||||
}
|
||||
|
||||
void AudioNetSink::write(AudioSample* samples, uint32_t numSamples)
|
||||
{
|
||||
if (m_type == SinkUDP)
|
||||
{
|
||||
int samplesIndex = 0;
|
||||
|
||||
if (m_bufferIndex + numSamples*sizeof(AudioSample) >= m_udpBlockSize) // fill remainder of buffer and send it
|
||||
{
|
||||
memcpy(&m_data[m_bufferIndex], &samples[samplesIndex], m_udpBlockSize - m_bufferIndex); // fill remainder of buffer
|
||||
m_udpSocket->writeDatagram((const char*)m_data, (qint64 ) m_udpBlockSize, m_address, m_port);
|
||||
m_bufferIndex = 0;
|
||||
samplesIndex += (m_udpBlockSize - m_bufferIndex) / sizeof(AudioSample);
|
||||
numSamples -= (m_udpBlockSize - m_bufferIndex) / sizeof(AudioSample);
|
||||
}
|
||||
|
||||
while (numSamples > m_udpBlockSize/sizeof(AudioSample)) // send directly from input without buffering
|
||||
{
|
||||
m_udpSocket->writeDatagram((const char*)&samples[samplesIndex], (qint64 ) m_udpBlockSize, m_address, m_port);
|
||||
samplesIndex += m_udpBlockSize/sizeof(AudioSample);
|
||||
numSamples -= m_udpBlockSize/sizeof(AudioSample);
|
||||
}
|
||||
|
||||
memcpy(&m_data[m_bufferIndex], &samples[samplesIndex], numSamples*sizeof(AudioSample));
|
||||
}
|
||||
else if (m_type == SinkRTP)
|
||||
{
|
||||
m_rtpBufferAudio->write((uint8_t *) samples, numSamples*2); // 2 x 16 bit sample
|
||||
}
|
||||
}
|
||||
//void AudioNetSink::write(AudioSample* samples, uint32_t numSamples)
|
||||
//{
|
||||
// if (m_type == SinkUDP)
|
||||
// {
|
||||
// int samplesIndex = 0;
|
||||
//
|
||||
// if (m_bufferIndex + numSamples*sizeof(AudioSample) >= m_udpBlockSize) // fill remainder of buffer and send it
|
||||
// {
|
||||
// memcpy(&m_data[m_bufferIndex], &samples[samplesIndex], m_udpBlockSize - m_bufferIndex); // fill remainder of buffer
|
||||
// m_udpSocket->writeDatagram((const char*)m_data, (qint64 ) m_udpBlockSize, m_address, m_port);
|
||||
// m_bufferIndex = 0;
|
||||
// samplesIndex += (m_udpBlockSize - m_bufferIndex) / sizeof(AudioSample);
|
||||
// numSamples -= (m_udpBlockSize - m_bufferIndex) / sizeof(AudioSample);
|
||||
// }
|
||||
//
|
||||
// while (numSamples > m_udpBlockSize/sizeof(AudioSample)) // send directly from input without buffering
|
||||
// {
|
||||
// m_udpSocket->writeDatagram((const char*)&samples[samplesIndex], (qint64 ) m_udpBlockSize, m_address, m_port);
|
||||
// samplesIndex += m_udpBlockSize/sizeof(AudioSample);
|
||||
// numSamples -= m_udpBlockSize/sizeof(AudioSample);
|
||||
// }
|
||||
//
|
||||
// memcpy(&m_data[m_bufferIndex], &samples[samplesIndex], numSamples*sizeof(AudioSample));
|
||||
// }
|
||||
// else if (m_type == SinkRTP)
|
||||
// {
|
||||
// m_rtpBufferAudio->write((uint8_t *) samples, numSamples*2); // 2 x 16 bit sample
|
||||
// }
|
||||
//}
|
||||
|
||||
void AudioNetSink::moveToThread(QThread *thread)
|
||||
{
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define SDRBASE_AUDIO_AUDIONETSINK_H_
|
||||
|
||||
#include "dsp/dsptypes.h"
|
||||
#include "audiocompressor.h"
|
||||
#include "export.h"
|
||||
|
||||
#include <QObject>
|
||||
@ -37,6 +38,13 @@ public:
|
||||
SinkRTP
|
||||
} SinkType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CodecL16,
|
||||
CodecPCMA,
|
||||
CodecPCMU
|
||||
} Codec;
|
||||
|
||||
AudioNetSink(QObject *parent); //!< without RTP
|
||||
AudioNetSink(QObject *parent, int sampleRate, bool stereo); //!< with RTP
|
||||
~AudioNetSink();
|
||||
@ -44,11 +52,11 @@ public:
|
||||
void setDestination(const QString& address, uint16_t port);
|
||||
void addDestination(const QString& address, uint16_t port);
|
||||
void deleteDestination(const QString& address, uint16_t port);
|
||||
void setParameters(bool stereo, int sampleRate);
|
||||
void setParameters(Codec codec, bool stereo, int sampleRate);
|
||||
|
||||
void write(qint16 sample);
|
||||
void write(qint16 lSample, qint16 rSample);
|
||||
void write(AudioSample* samples, uint32_t numSamples);
|
||||
//void write(AudioSample* samples, uint32_t numSamples);
|
||||
|
||||
bool isRTPCapable() const;
|
||||
bool selectType(SinkType type);
|
||||
@ -59,8 +67,10 @@ public:
|
||||
|
||||
protected:
|
||||
SinkType m_type;
|
||||
Codec m_codec;
|
||||
QUdpSocket *m_udpSocket;
|
||||
RTPSink *m_rtpBufferAudio;
|
||||
AudioCompressor m_audioCompressor;
|
||||
char m_data[65536];
|
||||
unsigned int m_bufferIndex;
|
||||
QHostAddress m_address;
|
||||
|
@ -29,6 +29,7 @@ AudioOutput::AudioOutput() :
|
||||
m_audioNetSink(0),
|
||||
m_copyAudioToUdp(false),
|
||||
m_udpChannelMode(UDPChannelLeft),
|
||||
m_udpChannelCodec(UDPCodecL16),
|
||||
m_audioUsageCount(0),
|
||||
m_onExit(false),
|
||||
m_audioFifos()
|
||||
@ -200,10 +201,12 @@ void AudioOutput::setUdpChannelMode(UDPChannelMode udpChannelMode)
|
||||
m_udpChannelMode = udpChannelMode;
|
||||
}
|
||||
|
||||
void AudioOutput::setUdpChannelFormat(bool stereo, int sampleRate)
|
||||
void AudioOutput::setUdpChannelFormat(UDPChannelCodec udpChannelCodec, bool stereo, int sampleRate)
|
||||
{
|
||||
m_udpChannelCodec = udpChannelCodec;
|
||||
|
||||
if (m_audioNetSink) {
|
||||
m_audioNetSink->setParameters(stereo, sampleRate);
|
||||
m_audioNetSink->setParameters((AudioNetSink::Codec) m_udpChannelCodec, stereo, sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,13 @@ public:
|
||||
UDPChannelStereo
|
||||
};
|
||||
|
||||
enum UDPChannelCodec
|
||||
{
|
||||
UDPCodecL16, //!< Linear 16 bit (no codec)
|
||||
UDPCodecALaw,
|
||||
UDPCodecULaw
|
||||
};
|
||||
|
||||
AudioOutput();
|
||||
virtual ~AudioOutput();
|
||||
|
||||
@ -58,7 +65,7 @@ public:
|
||||
void setUdpCopyToUDP(bool copyToUDP);
|
||||
void setUdpUseRTP(bool useRTP);
|
||||
void setUdpChannelMode(UDPChannelMode udpChannelMode);
|
||||
void setUdpChannelFormat(bool stereo, int sampleRate);
|
||||
void setUdpChannelFormat(UDPChannelCodec udpChannelCodec, bool stereo, int sampleRate);
|
||||
|
||||
private:
|
||||
QMutex m_mutex;
|
||||
@ -66,6 +73,7 @@ private:
|
||||
AudioNetSink* m_audioNetSink;
|
||||
bool m_copyAudioToUdp;
|
||||
UDPChannelMode m_udpChannelMode;
|
||||
UDPChannelCodec m_udpChannelCodec;
|
||||
uint m_audioUsageCount;
|
||||
bool m_onExit;
|
||||
|
||||
|
@ -76,24 +76,37 @@ void RTPSink::setPayloadInformation(PayloadType payloadType, int sampleRate)
|
||||
uint32_t timestampinc;
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
qDebug("RTPSink::setPayloadInformation: %d sampleRate: %d", payloadType, sampleRate);
|
||||
qDebug("RTPSink::setPayloadInformation: payloadType: %d sampleRate: %d", payloadType, sampleRate);
|
||||
|
||||
switch (payloadType)
|
||||
{
|
||||
case PayloadPCMA8:
|
||||
m_sampleBytes = 1;
|
||||
m_rtpSession.SetDefaultPayloadType(8);
|
||||
m_packetSamples = m_sampleRate / 50; // 20ms packet samples
|
||||
timestampinc = m_sampleRate / 50; // 8k -> 160 packets in 20ms
|
||||
break;
|
||||
case PayloadPCMU8:
|
||||
m_sampleBytes = 1;
|
||||
m_rtpSession.SetDefaultPayloadType(0);
|
||||
m_packetSamples = m_sampleRate / 50; // 20ms packet samples
|
||||
timestampinc = m_sampleRate / 50; // 8k -> 160 packets in 20ms
|
||||
break;
|
||||
case PayloadL16Stereo:
|
||||
m_sampleBytes = 4;
|
||||
m_rtpSession.SetDefaultPayloadType(96);
|
||||
m_packetSamples = m_sampleRate / 50; // 20ms packet samples
|
||||
timestampinc = m_sampleRate / 100;
|
||||
break;
|
||||
case PayloadL16Mono:
|
||||
default:
|
||||
m_sampleBytes = 2;
|
||||
m_rtpSession.SetDefaultPayloadType(96);
|
||||
m_packetSamples = m_sampleRate / 50; // 20ms packet samples
|
||||
timestampinc = m_sampleRate / 50;
|
||||
break;
|
||||
}
|
||||
|
||||
m_packetSamples = m_sampleRate/50; // 20ms packet samples
|
||||
m_bufferSize = m_packetSamples * m_sampleBytes;
|
||||
|
||||
if (m_byteBuffer) {
|
||||
@ -299,6 +312,10 @@ unsigned int RTPSink::elemLength(PayloadType payloadType)
|
||||
{
|
||||
switch (payloadType)
|
||||
{
|
||||
case PayloadPCMA8:
|
||||
case PayloadPCMU8:
|
||||
return sizeof(int8_t);
|
||||
break;
|
||||
case PayloadL16Stereo:
|
||||
return sizeof(int16_t);
|
||||
break;
|
||||
|
@ -42,6 +42,8 @@ public:
|
||||
{
|
||||
PayloadL16Mono,
|
||||
PayloadL16Stereo,
|
||||
PayloadPCMA8,
|
||||
PayloadPCMU8
|
||||
} PayloadType;
|
||||
|
||||
RTPSink(QUdpSocket *udpSocket, int sampleRate, bool stereo);
|
||||
|
@ -219,6 +219,7 @@ void AudioDialogX::updateOutputDisplay()
|
||||
ui->outputUDPCopy->setChecked(m_outputDeviceInfo.copyToUDP);
|
||||
ui->outputUDPUseRTP->setChecked(m_outputDeviceInfo.udpUseRTP);
|
||||
ui->outputUDPChannelMode->setCurrentIndex((int) m_outputDeviceInfo.udpChannelMode);
|
||||
ui->outputUDPChannelCodec->setCurrentIndex((int) m_outputDeviceInfo.udpChannelCodec);
|
||||
}
|
||||
|
||||
void AudioDialogX::updateOutputDeviceInfo()
|
||||
@ -229,5 +230,6 @@ void AudioDialogX::updateOutputDeviceInfo()
|
||||
m_outputDeviceInfo.copyToUDP = ui->outputUDPCopy->isChecked();
|
||||
m_outputDeviceInfo.udpUseRTP = ui->outputUDPUseRTP->isChecked();
|
||||
m_outputDeviceInfo.udpChannelMode = (AudioOutput::UDPChannelMode) ui->outputUDPChannelMode->currentIndex();
|
||||
m_outputDeviceInfo.udpChannelCodec = (AudioOutput::UDPChannelCodec) ui->outputUDPChannelCodec->currentIndex();
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,16 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>472</width>
|
||||
<height>349</height>
|
||||
<width>560</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>560</width>
|
||||
<height>400</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
@ -231,6 +237,34 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="outputUDPChannelCodec">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>85</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Encoding</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>L16</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PCMA/8k</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>PCMU/8k</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="outputUDPUseRTP">
|
||||
<property name="toolTip">
|
||||
|
Loading…
Reference in New Issue
Block a user