- ongoing work to ease jitter & RTT calculation
This commit is contained in:
@@ -22,413 +22,412 @@
|
||||
|
||||
using namespace MT;
|
||||
AudioStream::AudioStream(const CodecList::Settings& settings)
|
||||
:mPacketTime(0), mEncodedTime(0), mCodecSettings(settings),
|
||||
mRemoteTelephoneCodec(0), mRtpSession(), mTransmittingPayloadType(-1),
|
||||
mRtpSender(mStat)
|
||||
:mPacketTime(0), mEncodedTime(0), mCodecSettings(settings),
|
||||
mRemoteTelephoneCodec(0), mRtpSession(), mTransmittingPayloadType(-1),
|
||||
mRtpSender(mStat)
|
||||
{
|
||||
mOutputBuffer.setCapacity(16384);
|
||||
mCapturedAudio.setCapacity(16384);
|
||||
mCaptureResampler8.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 8000);
|
||||
mCaptureResampler16.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 16000);
|
||||
mCaptureResampler32.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 32000);
|
||||
mCaptureResampler48.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 48000);
|
||||
mOutputBuffer.setCapacity(16384);
|
||||
mCapturedAudio.setCapacity(16384);
|
||||
mCaptureResampler8.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 8000);
|
||||
mCaptureResampler16.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 16000);
|
||||
mCaptureResampler32.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 32000);
|
||||
mCaptureResampler48.start(AUDIO_CHANNELS, AUDIO_SAMPLERATE, 48000);
|
||||
|
||||
// Configure transmitter
|
||||
jrtplib::RTPExternalTransmissionParams params(&mRtpSender, 0);
|
||||
|
||||
jrtplib::RTPSessionParams sessionParams;
|
||||
sessionParams.SetAcceptOwnPackets(true);
|
||||
sessionParams.SetMaximumPacketSize(MT_MAXRTPPACKET);
|
||||
sessionParams.SetResolveLocalHostname(false);
|
||||
sessionParams.SetUsePollThread(false);
|
||||
sessionParams.SetOwnTimestampUnit(1/8000.0);
|
||||
mRtpSession.Create(sessionParams, ¶ms, jrtplib::RTPTransmitter::ExternalProto);
|
||||
mRtpDtmfSession.Create(sessionParams, ¶ms, jrtplib::RTPTransmitter::ExternalProto);
|
||||
|
||||
// Attach srtp session to sender
|
||||
mRtpSender.setSrtpSession(&mSrtpSession);
|
||||
//mRtpDump = new RtpDump("d:\\outgoing.rtp");
|
||||
//mRtpSender.setDumpWriter(mRtpDump);
|
||||
// Configure transmitter
|
||||
jrtplib::RTPExternalTransmissionParams params(&mRtpSender, 0);
|
||||
|
||||
jrtplib::RTPSessionParams sessionParams;
|
||||
sessionParams.SetAcceptOwnPackets(true);
|
||||
sessionParams.SetMaximumPacketSize(MT_MAXRTPPACKET);
|
||||
sessionParams.SetResolveLocalHostname(false);
|
||||
sessionParams.SetUsePollThread(false);
|
||||
sessionParams.SetOwnTimestampUnit(1/8000.0);
|
||||
mRtpSession.Create(sessionParams, ¶ms, jrtplib::RTPTransmitter::ExternalProto);
|
||||
mRtpDtmfSession.Create(sessionParams, ¶ms, jrtplib::RTPTransmitter::ExternalProto);
|
||||
|
||||
// Attach srtp session to sender
|
||||
mRtpSender.setSrtpSession(&mSrtpSession);
|
||||
//mRtpDump = new RtpDump("d:\\outgoing.rtp");
|
||||
//mRtpSender.setDumpWriter(mRtpDump);
|
||||
|
||||
#if defined(DUMP_SENDING_AUDIO)
|
||||
mSendingDump = std::make_shared<WavFileWriter>();
|
||||
mSendingDump->open("sending_audio.wav", 8000, 1);
|
||||
mSendingDump = std::make_shared<WavFileWriter>();
|
||||
mSendingDump->open("sending_audio.wav", 8000, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
AudioStream::~AudioStream()
|
||||
{
|
||||
ICELogInfo(<< "Delete AudioStream instance");
|
||||
if (mSendingDump)
|
||||
{
|
||||
mSendingDump->close();
|
||||
mSendingDump.reset();
|
||||
}
|
||||
ICELogInfo(<< "Delete AudioStream instance");
|
||||
if (mSendingDump)
|
||||
{
|
||||
mSendingDump->close();
|
||||
mSendingDump.reset();
|
||||
}
|
||||
|
||||
// Delete used rtp streams
|
||||
for (AudioStreamMap::iterator streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
delete streamIter->second;
|
||||
mStreamMap.clear();
|
||||
// Delete used rtp streams
|
||||
for (AudioStreamMap::iterator streamIter = mStreamMap.begin(); streamIter != mStreamMap.end(); ++streamIter)
|
||||
delete streamIter->second;
|
||||
mStreamMap.clear();
|
||||
|
||||
if (mRtpDtmfSession.IsActive())
|
||||
mRtpDtmfSession.Destroy();
|
||||
if (mRtpSession.IsActive())
|
||||
mRtpSession.Destroy();
|
||||
if (mRtpDtmfSession.IsActive())
|
||||
mRtpDtmfSession.Destroy();
|
||||
if (mRtpSession.IsActive())
|
||||
mRtpSession.Destroy();
|
||||
|
||||
#if defined(USE_RTPDUMP)
|
||||
if (mRtpDump)
|
||||
{
|
||||
mRtpDump->flush();
|
||||
delete mRtpDump;
|
||||
}
|
||||
if (mRtpDump)
|
||||
{
|
||||
mRtpDump->flush();
|
||||
delete mRtpDump;
|
||||
}
|
||||
#endif
|
||||
|
||||
mCaptureResampler8.stop();
|
||||
mCaptureResampler16.stop();
|
||||
mCaptureResampler32.stop();
|
||||
mCaptureResampler48.stop();
|
||||
ICELogInfo(<< "Encoded " << mEncodedTime << " milliseconds of audio");
|
||||
mCaptureResampler8.stop();
|
||||
mCaptureResampler16.stop();
|
||||
mCaptureResampler32.stop();
|
||||
mCaptureResampler48.stop();
|
||||
ICELogInfo(<< "Encoded " << mEncodedTime << " milliseconds of audio");
|
||||
|
||||
if (mDumpStreams.mStreamForRecordingIncoming)
|
||||
mDumpStreams.mStreamForRecordingIncoming->close();
|
||||
if (mDumpStreams.mStreamForReadingOutgoing)
|
||||
mDumpStreams.mStreamForReadingOutgoing->close();
|
||||
if (mDumpStreams.mStreamForRecordingIncoming)
|
||||
mDumpStreams.mStreamForRecordingIncoming->close();
|
||||
if (mDumpStreams.mStreamForReadingOutgoing)
|
||||
mDumpStreams.mStreamForReadingOutgoing->close();
|
||||
|
||||
if (mFinalStatistics)
|
||||
*mFinalStatistics = mStat;
|
||||
if (mFinalStatistics)
|
||||
*mFinalStatistics = mStat;
|
||||
|
||||
ICELogInfo(<< mStat.toString());
|
||||
ICELogInfo(<< mStat.toString());
|
||||
}
|
||||
|
||||
void AudioStream::setDestination(const RtpPair<InternetAddress>& dest)
|
||||
{
|
||||
Lock l(mMutex);
|
||||
Stream::setDestination(dest);
|
||||
mRtpSender.setDestination(dest);
|
||||
Lock l(mMutex);
|
||||
Stream::setDestination(dest);
|
||||
mRtpSender.setDestination(dest);
|
||||
}
|
||||
|
||||
void AudioStream::setTransmittingCodec(Codec::Factory& factory, int payloadType)
|
||||
{
|
||||
ICELogInfo(<< "Selected codec " << factory.name() << "/" << factory.samplerate() << " for transmitting");
|
||||
ICELogInfo(<< "Selected codec " << factory.name() << "/" << factory.samplerate() << " for transmitting");
|
||||
|
||||
Lock l(mMutex);
|
||||
mTransmittingCodec = factory.create();
|
||||
mTransmittingPayloadType = payloadType;
|
||||
if (mRtpSession.IsActive())
|
||||
mRtpSession.SetTimestampUnit(1.0 / mTransmittingCodec->samplerate());
|
||||
Lock l(mMutex);
|
||||
mTransmittingCodec = factory.create();
|
||||
mTransmittingPayloadType = payloadType;
|
||||
if (mRtpSession.IsActive())
|
||||
mRtpSession.SetTimestampUnit(1.0 / mTransmittingCodec->samplerate());
|
||||
}
|
||||
|
||||
PCodec AudioStream::transmittingCodec()
|
||||
{
|
||||
Lock l(mMutex);
|
||||
return mTransmittingCodec;
|
||||
Lock l(mMutex);
|
||||
return mTransmittingCodec;
|
||||
}
|
||||
|
||||
void AudioStream::addData(const void* buffer, int bytes)
|
||||
{
|
||||
assert(bytes == AUDIO_MIC_BUFFER_SIZE);
|
||||
assert(bytes == AUDIO_MIC_BUFFER_SIZE);
|
||||
|
||||
// Read predefined audio if configured
|
||||
if (mDumpStreams.mStreamForReadingOutgoing)
|
||||
{
|
||||
if (mDumpStreams.mStreamForReadingOutgoing->isOpened())
|
||||
mDumpStreams.mStreamForReadingOutgoing->read(const_cast<void*>(buffer), bytes);
|
||||
}
|
||||
|
||||
// Read mirrored audio if needed
|
||||
if (mMirror && mMirrorPrebuffered)
|
||||
mMirrorBuffer.read(const_cast<void*>(buffer), bytes);
|
||||
|
||||
if (mMediaObserver)
|
||||
mMediaObserver->onMedia(buffer, bytes, MT::Stream::MediaDirection::Outgoing, this, mMediaObserverTag);
|
||||
|
||||
Codec* codec = nullptr;
|
||||
{
|
||||
Lock l(mMutex);
|
||||
codec = mTransmittingCodec.get();
|
||||
if (nullptr == codec) {
|
||||
// ICELogDebug(<< "No transmitting codec selected.");
|
||||
return;
|
||||
// Read predefined audio if configured
|
||||
if (mDumpStreams.mStreamForReadingOutgoing)
|
||||
{
|
||||
if (mDumpStreams.mStreamForReadingOutgoing->isOpened())
|
||||
mDumpStreams.mStreamForReadingOutgoing->read(const_cast<void*>(buffer), bytes);
|
||||
}
|
||||
}
|
||||
|
||||
// Resample
|
||||
unsigned dstlen = unsigned(float(codec->samplerate() / float(AUDIO_SAMPLERATE)) * bytes);
|
||||
Audio::Resampler* r = nullptr;
|
||||
switch (codec->samplerate())
|
||||
{
|
||||
case 8000: r = &mCaptureResampler8; break;
|
||||
case 16000: r = &mCaptureResampler16; break;
|
||||
case 32000: r = &mCaptureResampler32; break;
|
||||
case 48000: r = &mCaptureResampler48; break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
// Read mirrored audio if needed
|
||||
if (mMirror && mMirrorPrebuffered)
|
||||
mMirrorBuffer.read(const_cast<void*>(buffer), bytes);
|
||||
|
||||
size_t processedInput = 0;
|
||||
dstlen = r->processBuffer(buffer, bytes, processedInput, mResampleBuffer, dstlen);
|
||||
// ProcessedInput output value is ignored - because sample rate of input is always 8/16/32/48K - so all buffer is processed
|
||||
if (mMediaObserver)
|
||||
mMediaObserver->onMedia(buffer, bytes, MT::Stream::MediaDirection::Outgoing, this, mMediaObserverTag);
|
||||
|
||||
// See if we need stereo <-> mono conversions
|
||||
unsigned stereolen = 0;
|
||||
if (codec->channels() != AUDIO_CHANNELS)
|
||||
{
|
||||
if (codec->channels() == 2)
|
||||
stereolen = Audio::ChannelConverter::monoToStereo(mResampleBuffer, dstlen, mStereoBuffer, dstlen * 2);
|
||||
Codec* codec = nullptr;
|
||||
{
|
||||
Lock l(mMutex);
|
||||
codec = mTransmittingCodec.get();
|
||||
if (nullptr == codec) {
|
||||
// ICELogDebug(<< "No transmitting codec selected.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Resample
|
||||
unsigned dstlen = unsigned(float(codec->samplerate() / float(AUDIO_SAMPLERATE)) * bytes);
|
||||
Audio::Resampler* r = nullptr;
|
||||
switch (codec->samplerate())
|
||||
{
|
||||
case 8000: r = &mCaptureResampler8; break;
|
||||
case 16000: r = &mCaptureResampler16; break;
|
||||
case 32000: r = &mCaptureResampler32; break;
|
||||
case 48000: r = &mCaptureResampler48; break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
size_t processedInput = 0;
|
||||
dstlen = r->processBuffer(buffer, bytes, processedInput, mResampleBuffer, dstlen);
|
||||
// ProcessedInput output value is ignored - because sample rate of input is always 8/16/32/48K - so all buffer is processed
|
||||
|
||||
// See if we need stereo <-> mono conversions
|
||||
unsigned stereolen = 0;
|
||||
if (codec->channels() != AUDIO_CHANNELS)
|
||||
{
|
||||
if (codec->channels() == 2)
|
||||
stereolen = Audio::ChannelConverter::monoToStereo(mResampleBuffer, dstlen, mStereoBuffer, dstlen * 2);
|
||||
else
|
||||
dstlen = Audio::ChannelConverter::stereoToMono(mResampleBuffer, dstlen, mResampleBuffer, dstlen / 2);
|
||||
}
|
||||
|
||||
// See if inband dtmf audio should be sent instead
|
||||
ByteBuffer dtmf;
|
||||
if (mDtmfContext.type() == DtmfContext::Dtmf_Inband && mDtmfContext.getInband(AUDIO_MIC_BUFFER_LENGTH, codec->samplerate(), dtmf))
|
||||
mCapturedAudio.add(dtmf.data(), dtmf.size());
|
||||
else
|
||||
dstlen = Audio::ChannelConverter::stereoToMono(mResampleBuffer, dstlen, mResampleBuffer, dstlen / 2);
|
||||
}
|
||||
mCapturedAudio.add(stereolen ? mStereoBuffer : mResampleBuffer, stereolen ? stereolen : dstlen);
|
||||
|
||||
// See if inband dtmf audio should be sent instead
|
||||
ByteBuffer dtmf;
|
||||
if (mDtmfContext.type() == DtmfContext::Dtmf_Inband && mDtmfContext.getInband(AUDIO_MIC_BUFFER_LENGTH, codec->samplerate(), dtmf))
|
||||
mCapturedAudio.add(dtmf.data(), dtmf.size());
|
||||
else
|
||||
mCapturedAudio.add(stereolen ? mStereoBuffer : mResampleBuffer, stereolen ? stereolen : dstlen);
|
||||
|
||||
// See if it is time to send RFC2833 tone
|
||||
ByteBuffer rfc2833, stopPacket;
|
||||
if (mDtmfContext.type() == DtmfContext::Dtmf_Rfc2833 && mDtmfContext.getRfc2833(AUDIO_MIC_BUFFER_LENGTH, rfc2833, stopPacket))
|
||||
{
|
||||
if (rfc2833.size())
|
||||
mRtpDtmfSession.SendPacket(rfc2833.data(), rfc2833.size(), mRemoteTelephoneCodec, true, AUDIO_MIC_BUFFER_LENGTH * 8);
|
||||
|
||||
if (stopPacket.size())
|
||||
// See if it is time to send RFC2833 tone
|
||||
ByteBuffer rfc2833, stopPacket;
|
||||
if (mDtmfContext.type() == DtmfContext::Dtmf_Rfc2833 && mDtmfContext.getRfc2833(AUDIO_MIC_BUFFER_LENGTH, rfc2833, stopPacket))
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
mRtpDtmfSession.SendPacket(stopPacket.data(), stopPacket.size(), mRemoteTelephoneCodec, true, AUDIO_MIC_BUFFER_LENGTH * 8);
|
||||
if (rfc2833.size())
|
||||
mRtpDtmfSession.SendPacket(rfc2833.data(), rfc2833.size(), mRemoteTelephoneCodec, true, AUDIO_MIC_BUFFER_LENGTH * 8);
|
||||
|
||||
if (stopPacket.size())
|
||||
{
|
||||
for (int i=0; i<3; i++)
|
||||
mRtpDtmfSession.SendPacket(stopPacket.data(), stopPacket.size(), mRemoteTelephoneCodec, true, AUDIO_MIC_BUFFER_LENGTH * 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int processed = 0;
|
||||
int encodedTime = 0;
|
||||
int packetTime = mPacketTime ? mPacketTime : codec->frameTime();
|
||||
|
||||
// Make stereo version if required
|
||||
for (int i=0; i<mCapturedAudio.filled() / codec->pcmLength(); i++)
|
||||
{
|
||||
if (mSendingDump)
|
||||
mSendingDump->write((const char*)mCapturedAudio.data() + codec->pcmLength() * i, codec->pcmLength());
|
||||
int processed = 0;
|
||||
int encodedTime = 0;
|
||||
int packetTime = mPacketTime ? mPacketTime : codec->frameTime();
|
||||
|
||||
int produced;
|
||||
produced = codec->encode((const char*)mCapturedAudio.data() + codec->pcmLength()*i,
|
||||
codec->pcmLength(), mFrameBuffer, MT_MAXAUDIOFRAME);
|
||||
// Make stereo version if required
|
||||
for (int i=0; i<mCapturedAudio.filled() / codec->pcmLength(); i++)
|
||||
{
|
||||
if (mSendingDump)
|
||||
mSendingDump->write((const char*)mCapturedAudio.data() + codec->pcmLength() * i, codec->pcmLength());
|
||||
|
||||
int produced;
|
||||
produced = codec->encode((const char*)mCapturedAudio.data() + codec->pcmLength()*i,
|
||||
codec->pcmLength(), mFrameBuffer, MT_MAXAUDIOFRAME);
|
||||
|
||||
// Counter of processed input bytes of raw pcm data from microphone
|
||||
processed += codec->pcmLength();
|
||||
encodedTime += codec->frameTime();
|
||||
mEncodedTime += codec->frameTime();
|
||||
// Counter of processed input bytes of raw pcm data from microphone
|
||||
processed += codec->pcmLength();
|
||||
encodedTime += codec->frameTime();
|
||||
mEncodedTime += codec->frameTime();
|
||||
|
||||
if (produced)
|
||||
{
|
||||
mEncodedAudio.appendBuffer(mFrameBuffer, produced);
|
||||
if (packetTime <= encodedTime)
|
||||
{
|
||||
// Time to send packet
|
||||
ICELogMedia(<< "Sending RTP packet pt = " << mTransmittingPayloadType << ", plength = " << (int)mEncodedAudio.size());
|
||||
mRtpSession.SendPacketEx(mEncodedAudio.data(), mEncodedAudio.size(), mTransmittingPayloadType, false,
|
||||
packetTime * codec->samplerate()/1000, 0, NULL, 0);
|
||||
mEncodedAudio.clear();
|
||||
encodedTime = 0;
|
||||
}
|
||||
if (produced)
|
||||
{
|
||||
mEncodedAudio.appendBuffer(mFrameBuffer, produced);
|
||||
if (packetTime <= encodedTime)
|
||||
{
|
||||
// Time to send packet
|
||||
ICELogMedia(<< "Sending RTP packet pt = " << mTransmittingPayloadType << ", plength = " << (int)mEncodedAudio.size());
|
||||
mRtpSession.SendPacketEx(mEncodedAudio.data(), mEncodedAudio.size(), mTransmittingPayloadType, false,
|
||||
packetTime * codec->samplerate()/1000, 0, NULL, 0);
|
||||
mEncodedAudio.clear();
|
||||
encodedTime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (processed > 0)
|
||||
mCapturedAudio.erase(processed);
|
||||
if (processed > 0)
|
||||
mCapturedAudio.erase(processed);
|
||||
}
|
||||
|
||||
void AudioStream::copyDataTo(Audio::Mixer& mixer, int needed)
|
||||
{
|
||||
// Local audio mixer - used to send audio to media observer
|
||||
Audio::Mixer localMixer;
|
||||
Audio::DataWindow forObserver;
|
||||
// Local audio mixer - used to send audio to media observer
|
||||
Audio::Mixer localMixer;
|
||||
Audio::DataWindow forObserver;
|
||||
|
||||
// Iterate
|
||||
for (auto& streamIter: mStreamMap)
|
||||
{
|
||||
Audio::DataWindow w;
|
||||
w.setCapacity(32768);
|
||||
|
||||
SingleAudioStream* sas = streamIter.second;
|
||||
if (sas)
|
||||
// Iterate
|
||||
for (auto& streamIter: mStreamMap)
|
||||
{
|
||||
sas->copyPcmTo(w, needed);
|
||||
Audio::DataWindow w;
|
||||
w.setCapacity(32768);
|
||||
|
||||
// Provide mirroring if needed
|
||||
if (mMirror)
|
||||
{
|
||||
mMirrorBuffer.add(w.data(), w.filled());
|
||||
if (!mMirrorPrebuffered)
|
||||
mMirrorPrebuffered = mMirrorBuffer.filled() >= MT_MIRROR_PREBUFFER;
|
||||
}
|
||||
|
||||
if (!(state() & (int)StreamState::Receiving))
|
||||
w.zero(needed);
|
||||
|
||||
// Check if we do not need input from this stream
|
||||
if (w.filled())
|
||||
{
|
||||
if (mDumpStreams.mStreamForRecordingIncoming)
|
||||
SingleAudioStream* sas = streamIter.second;
|
||||
if (sas)
|
||||
{
|
||||
if (mDumpStreams.mStreamForRecordingIncoming->isOpened())
|
||||
mDumpStreams.mStreamForRecordingIncoming->write(w.data(), w.filled());
|
||||
sas->copyPcmTo(w, needed);
|
||||
|
||||
// Provide mirroring if needed
|
||||
if (mMirror)
|
||||
{
|
||||
mMirrorBuffer.add(w.data(), w.filled());
|
||||
if (!mMirrorPrebuffered)
|
||||
mMirrorPrebuffered = mMirrorBuffer.filled() >= MT_MIRROR_PREBUFFER;
|
||||
}
|
||||
|
||||
if (!(state() & (int)StreamState::Receiving))
|
||||
w.zero(needed);
|
||||
|
||||
// Check if we do not need input from this stream
|
||||
if (w.filled())
|
||||
{
|
||||
if (mDumpStreams.mStreamForRecordingIncoming)
|
||||
{
|
||||
if (mDumpStreams.mStreamForRecordingIncoming->isOpened())
|
||||
mDumpStreams.mStreamForRecordingIncoming->write(w.data(), w.filled());
|
||||
}
|
||||
mixer.addPcm(this, streamIter.first, w, AUDIO_SAMPLERATE, false);
|
||||
|
||||
if (mMediaObserver)
|
||||
localMixer.addPcm(this, streamIter.first, w, AUDIO_SAMPLERATE, false);
|
||||
}
|
||||
}
|
||||
mixer.addPcm(this, streamIter.first, w, AUDIO_SAMPLERATE, false);
|
||||
|
||||
if (mMediaObserver)
|
||||
localMixer.addPcm(this, streamIter.first, w, AUDIO_SAMPLERATE, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mMediaObserver)
|
||||
{
|
||||
localMixer.mixAndGetPcm(forObserver);
|
||||
mMediaObserver->onMedia(forObserver.data(), forObserver.capacity(), MT::Stream::MediaDirection::Incoming, this, mMediaObserverTag);
|
||||
}
|
||||
if (mMediaObserver)
|
||||
{
|
||||
localMixer.mixAndGetPcm(forObserver);
|
||||
mMediaObserver->onMedia(forObserver.data(), forObserver.capacity(), MT::Stream::MediaDirection::Incoming, this, mMediaObserverTag);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStream::dataArrived(PDatagramSocket s, const void* buffer, int length, InternetAddress& source)
|
||||
{
|
||||
jrtplib::RTPIPv6Address addr6;
|
||||
jrtplib::RTPIPv4Address addr4;
|
||||
jrtplib::RTPExternalTransmissionInfo* info = dynamic_cast<jrtplib::RTPExternalTransmissionInfo*>(mRtpSession.GetTransmissionInfo());
|
||||
assert(info);
|
||||
|
||||
// Drop RTP packets if stream is not receiving now; let RTCP go
|
||||
if (!(state() & (int)StreamState::Receiving) && RtpHelper::isRtp(buffer, length))
|
||||
{
|
||||
ICELogMedia(<< "Stream is not allowed to receive RTP stream. Ignore the packet");
|
||||
return;
|
||||
}
|
||||
jrtplib::RTPIPv6Address addr6;
|
||||
jrtplib::RTPIPv4Address addr4;
|
||||
jrtplib::RTPExternalTransmissionInfo* info = dynamic_cast<jrtplib::RTPExternalTransmissionInfo*>(mRtpSession.GetTransmissionInfo());
|
||||
assert(info);
|
||||
|
||||
// Copy incoming data to temp buffer to perform possible srtp unprotect
|
||||
int receiveLength = length;
|
||||
memcpy(mReceiveBuffer, buffer, length);
|
||||
|
||||
bool srtpResult;
|
||||
if (mSrtpSession.active())
|
||||
{
|
||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, &receiveLength);
|
||||
else
|
||||
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, &receiveLength);
|
||||
if (!srtpResult)
|
||||
// Drop RTP packets if stream is not receiving now; let RTCP go
|
||||
if (!(state() & (int)StreamState::Receiving) && RtpHelper::isRtpOrRtcp(buffer, length))
|
||||
{
|
||||
ICELogError(<<"Cannot decrypt SRTP packet.");
|
||||
return;
|
||||
ICELogMedia(<< "Stream is not allowed to receive RTP stream. Ignore the RT(C)P packet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//ICELogDebug(<< "Packet no: " << RtpHelper::findPacketNo(mReceiveBuffer, receiveLength));
|
||||
// Copy incoming data to temp buffer to perform possible srtp unprotect
|
||||
int receiveLength = length;
|
||||
memcpy(mReceiveBuffer, buffer, length);
|
||||
|
||||
switch (source.family())
|
||||
{
|
||||
case AF_INET:
|
||||
addr4.SetIP(source.sockaddr4()->sin_addr.s_addr);
|
||||
addr4.SetPort(source.port());
|
||||
ICELogMedia(<< "Injecting RTP/RTCP packet into jrtplib");
|
||||
info->GetPacketInjector()->InjectRTPorRTCP(mReceiveBuffer, receiveLength, addr4);
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
addr6.SetIP(source.sockaddr6()->sin6_addr);
|
||||
addr6.SetPort(source.port());
|
||||
ICELogMedia(<< "Injecting RTP/RTCP packet into jrtplib");
|
||||
info->GetPacketInjector()->InjectRTPorRTCP(mReceiveBuffer, receiveLength, addr6);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
mStat.mReceived += length;
|
||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||
{
|
||||
if (!mStat.mFirstRtpTime.is_initialized())
|
||||
mStat.mFirstRtpTime = std::chrono::system_clock::now();
|
||||
mStat.mReceivedRtp++;
|
||||
}
|
||||
else
|
||||
mStat.mReceivedRtcp++;
|
||||
|
||||
mRtpSession.Poll(); // maybe it is extra with external transmitter
|
||||
bool hasData = mRtpSession.GotoFirstSourceWithData();
|
||||
while (hasData)
|
||||
{
|
||||
std::shared_ptr<jrtplib::RTPPacket> packet(mRtpSession.GetNextPacket());
|
||||
if (packet)
|
||||
bool srtpResult;
|
||||
if (mSrtpSession.active())
|
||||
{
|
||||
ICELogMedia(<< "jrtplib returned packet");
|
||||
// Find right handler for rtp stream
|
||||
SingleAudioStream* rtpStream = nullptr;
|
||||
AudioStreamMap::iterator streamIter = mStreamMap.find(packet->GetSSRC());
|
||||
if (streamIter == mStreamMap.end())
|
||||
mStreamMap[packet->GetSSRC()] = rtpStream = new SingleAudioStream(mCodecSettings, mStat);
|
||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||
srtpResult = mSrtpSession.unprotectRtp(mReceiveBuffer, &receiveLength);
|
||||
else
|
||||
rtpStream = streamIter->second;
|
||||
|
||||
// Process incoming data packet
|
||||
rtpStream->process(packet);
|
||||
|
||||
double rtt = mRtpSession.GetCurrentSourceInfo()->INF_GetRoundtripTime().GetDouble();
|
||||
if (rtt > 0)
|
||||
mStat.mRttDelay.process(rtt);
|
||||
srtpResult = mSrtpSession.unprotectRtcp(mReceiveBuffer, &receiveLength);
|
||||
if (!srtpResult)
|
||||
{
|
||||
ICELogError(<<"Cannot decrypt SRTP packet.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (source.family())
|
||||
{
|
||||
case AF_INET:
|
||||
addr4.SetIP(source.sockaddr4()->sin_addr.s_addr);
|
||||
addr4.SetPort(source.port());
|
||||
ICELogMedia(<< "Injecting RTP/RTCP packet into jrtplib");
|
||||
info->GetPacketInjector()->InjectRTPorRTCP(mReceiveBuffer, receiveLength, addr4);
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
addr6.SetIP(source.sockaddr6()->sin6_addr);
|
||||
addr6.SetPort(source.port());
|
||||
ICELogMedia(<< "Injecting RTP/RTCP packet into jrtplib");
|
||||
info->GetPacketInjector()->InjectRTPorRTCP(mReceiveBuffer, receiveLength, addr6);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
mStat.mReceived += length;
|
||||
if (RtpHelper::isRtp(mReceiveBuffer, receiveLength))
|
||||
{
|
||||
if (!mStat.mFirstRtpTime.is_initialized())
|
||||
mStat.mFirstRtpTime = std::chrono::system_clock::now();
|
||||
mStat.mReceivedRtp++;
|
||||
}
|
||||
else
|
||||
mStat.mReceivedRtcp++;
|
||||
|
||||
mRtpSession.Poll(); // maybe it is extra with external transmitter
|
||||
bool hasData = mRtpSession.GotoFirstSourceWithData();
|
||||
while (hasData)
|
||||
{
|
||||
std::shared_ptr<jrtplib::RTPPacket> packet(mRtpSession.GetNextPacket());
|
||||
if (packet)
|
||||
{
|
||||
ICELogMedia(<< "jrtplib returned packet");
|
||||
|
||||
// Find right handler for rtp stream
|
||||
SingleAudioStream* rtpStream = nullptr;
|
||||
AudioStreamMap::iterator streamIter = mStreamMap.find(packet->GetSSRC());
|
||||
if (streamIter == mStreamMap.end())
|
||||
mStreamMap[packet->GetSSRC()] = rtpStream = new SingleAudioStream(mCodecSettings, mStat);
|
||||
else
|
||||
rtpStream = streamIter->second;
|
||||
|
||||
// Process incoming data packet
|
||||
rtpStream->process(packet);
|
||||
|
||||
double rtt = mRtpSession.GetCurrentSourceInfo()->INF_GetRoundtripTime().GetDouble();
|
||||
if (rtt > 0)
|
||||
mStat.mRttDelay.process(rtt);
|
||||
}
|
||||
hasData = mRtpSession.GotoNextSourceWithData();
|
||||
}
|
||||
hasData = mRtpSession.GotoNextSourceWithData();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStream::setState(unsigned state)
|
||||
{
|
||||
Stream::setState(state);
|
||||
Stream::setState(state);
|
||||
}
|
||||
|
||||
void AudioStream::setTelephoneCodec(int payloadType)
|
||||
{
|
||||
mRemoteTelephoneCodec = payloadType;
|
||||
mRemoteTelephoneCodec = payloadType;
|
||||
}
|
||||
|
||||
void AudioStream::setSocket(const RtpPair<PDatagramSocket>& socket)
|
||||
{
|
||||
Stream::setSocket(socket);
|
||||
mRtpSender.setSocket(socket);
|
||||
Stream::setSocket(socket);
|
||||
mRtpSender.setSocket(socket);
|
||||
}
|
||||
|
||||
DtmfContext& AudioStream::queueOfDtmf()
|
||||
{
|
||||
return mDtmfContext;
|
||||
return mDtmfContext;
|
||||
}
|
||||
|
||||
void AudioStream::readFile(const Audio::PWavFileReader& stream, MediaDirection direction)
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case MediaDirection::Outgoing: mDumpStreams.mStreamForReadingOutgoing = stream; break;
|
||||
case MediaDirection::Incoming: mDumpStreams.mStreamForReadingIncoming = stream; break;
|
||||
}
|
||||
switch (direction)
|
||||
{
|
||||
case MediaDirection::Outgoing: mDumpStreams.mStreamForReadingOutgoing = stream; break;
|
||||
case MediaDirection::Incoming: mDumpStreams.mStreamForReadingIncoming = stream; break;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStream::writeFile(const Audio::PWavFileWriter& writer, MediaDirection direction)
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case MediaDirection::Outgoing: mDumpStreams.mStreamForRecordingOutgoing = writer; break;
|
||||
case MediaDirection::Incoming: mDumpStreams.mStreamForRecordingIncoming = writer; break;
|
||||
}
|
||||
switch (direction)
|
||||
{
|
||||
case MediaDirection::Outgoing: mDumpStreams.mStreamForRecordingOutgoing = writer; break;
|
||||
case MediaDirection::Incoming: mDumpStreams.mStreamForRecordingIncoming = writer; break;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStream::setupMirror(bool enable)
|
||||
{
|
||||
if (!mMirror && enable)
|
||||
{
|
||||
mMirrorBuffer.setCapacity(MT_MIRROR_CAPACITY);
|
||||
mMirrorPrebuffered = false;
|
||||
}
|
||||
mMirror = enable;
|
||||
if (!mMirror && enable)
|
||||
{
|
||||
mMirrorBuffer.setCapacity(MT_MIRROR_CAPACITY);
|
||||
mMirrorPrebuffered = false;
|
||||
}
|
||||
mMirror = enable;
|
||||
}
|
||||
|
||||
void AudioStream::setFinalStatisticsOutput(Statistics* stats)
|
||||
{
|
||||
mFinalStatistics = stats;
|
||||
mFinalStatistics = stats;
|
||||
}
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
namespace MT
|
||||
{
|
||||
|
||||
class AudioStream: public Stream
|
||||
{
|
||||
public:
|
||||
|
||||
class AudioStream: public Stream
|
||||
{
|
||||
public:
|
||||
AudioStream(const CodecList::Settings& codecSettings);
|
||||
~AudioStream();
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace MT
|
||||
void setTransmittingCodec(Codec::Factory& factory, int payloadType) override;
|
||||
PCodec transmittingCodec();
|
||||
|
||||
// Called to queue data captured from microphone.
|
||||
// Called to queue data captured from microphone.
|
||||
// Buffer holds 16bits PCM data with AUDIO_SAMPLERATE rate and AUDIO_CHANNELS channels.
|
||||
void addData(const void* buffer, int length);
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace MT
|
||||
void setState(unsigned state) override;
|
||||
|
||||
void setTelephoneCodec(int payloadType);
|
||||
DtmfContext& queueOfDtmf();
|
||||
DtmfContext& queueOfDtmf();
|
||||
|
||||
void readFile(const Audio::PWavFileReader& stream, MediaDirection direction) override;
|
||||
void writeFile(const Audio::PWavFileWriter& writer, MediaDirection direction) override;
|
||||
@@ -59,7 +59,7 @@ namespace MT
|
||||
|
||||
void setFinalStatisticsOutput(Statistics* stats);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
Audio::DataWindow mCapturedAudio; // Data from microphone
|
||||
Audio::DataWindow mStereoCapturedAudio;
|
||||
char mIncomingPcmBuffer[AUDIO_MIC_BUFFER_SIZE]; // Temporary buffer to allow reading from file
|
||||
@@ -83,18 +83,18 @@ namespace MT
|
||||
RtpDump* mRtpDump = nullptr;
|
||||
#endif
|
||||
Audio::Resampler mCaptureResampler8,
|
||||
mCaptureResampler16,
|
||||
mCaptureResampler32,
|
||||
mCaptureResampler48;
|
||||
DtmfContext mDtmfContext;
|
||||
mCaptureResampler16,
|
||||
mCaptureResampler32,
|
||||
mCaptureResampler48;
|
||||
DtmfContext mDtmfContext;
|
||||
char mReceiveBuffer[MAX_VALID_UDPPACKET_SIZE];
|
||||
|
||||
struct
|
||||
{
|
||||
Audio::PWavFileWriter mStreamForRecordingIncoming,
|
||||
mStreamForRecordingOutgoing;
|
||||
Audio::PWavFileReader mStreamForReadingIncoming,
|
||||
mStreamForReadingOutgoing;
|
||||
Audio::PWavFileWriter mStreamForRecordingIncoming,
|
||||
mStreamForRecordingOutgoing;
|
||||
Audio::PWavFileReader mStreamForReadingIncoming,
|
||||
mStreamForReadingOutgoing;
|
||||
} mDumpStreams;
|
||||
|
||||
Audio::PWavFileWriter mSendingDump;
|
||||
@@ -106,7 +106,7 @@ namespace MT
|
||||
Statistics* mFinalStatistics = nullptr;
|
||||
|
||||
bool decryptSrtp(void* data, int* len);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -40,7 +40,7 @@ PStream Terminal::createStream(int type, VariantMap& /*config*/)
|
||||
switch (type)
|
||||
{
|
||||
case Stream::Audio:
|
||||
result = PStream(new AudioStream(MT::CodecList::Settings::DefaultSettings));
|
||||
result = std::make_shared<AudioStream>(MT::CodecList::Settings::DefaultSettings);
|
||||
mAudioList.add(result);
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user