Merge remote-tracking branch 'origin/stable'

This commit is contained in:
Dmytro Bogovych
2022-06-03 08:46:24 +03:00
1332 changed files with 134626 additions and 292 deletions

View File

@@ -171,10 +171,13 @@ void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
#endif
std::string transport = d["transport"].asString();
config()[CONFIG_TRANSPORT] = (transport == "any") ? 0 : (transport == "udp" ? 1 : (transport == "tcp" ? 2 : 3));
config()[CONFIG_TRANSPORT] = (transport == "any") ? TransportType_Any : (transport == "udp" ? TransportType_Udp : (transport == "tcp" ? TransportType_Tcp : TransportType_Tls));
config()[CONFIG_IPV4] = d["ipv4"].asBool();
config()[CONFIG_IPV6] = d["ipv6"].asBool();
if (transport == "tls")
config()[CONFIG_SIPS] = true;
// Log file
std::string logfile = d["logfile"].asString();
ice::Logger& logger = ice::GLogger;
@@ -186,10 +189,12 @@ void AgentImpl::processConfig(JsonCpp::Value &d, JsonCpp::Value &answer)
mUseNativeAudio = d["nativeaudio"].asBool();
config()[CONFIG_OWN_DNS] = d["dns_servers"].asString();
config()[CONFIG_SIPS] = d["secure"].asBool();
answer["status"] = Status_Ok;
}
void AgentImpl::processStart(JsonCpp::Value& /*request*/, JsonCpp::Value &answer)
void AgentImpl::processStart(JsonCpp::Value& request, JsonCpp::Value &answer)
{
std::unique_lock<std::recursive_mutex> l(mAgentMutex);
if (mThread)
@@ -198,6 +203,9 @@ void AgentImpl::processStart(JsonCpp::Value& /*request*/, JsonCpp::Value &answer
return; // Started already
}
// Process config (can be sent via start command as well)
// processConfig(request, answer);
// Start socket thread
SocketHeap::instance().start();

View File

@@ -25,7 +25,6 @@
namespace Audio
{
class AndroidEnumerator: public Enumerator
{
public:

View File

@@ -0,0 +1,245 @@
#include "Audio_AndroidOboe.h"
#include "../helper/HL_Sync.h"
#include "../helper/HL_Log.h"
#include <mutex>
#include <iostream>
#include <stdexcept>
#include "../helper/HL_String.h"
#include "../helper/HL_Time.h"
#ifdef TARGET_ANDROID
#define LOG_SUBSYSTEM "Audio"
using namespace Audio;
// -------------------- AndroidEnumerator -----------------------------
AndroidEnumerator::AndroidEnumerator()
{}
AndroidEnumerator::~AndroidEnumerator()
{}
int AndroidEnumerator::indexOfDefaultDevice()
{
return 0;
}
int AndroidEnumerator::count()
{
return 1;
}
int AndroidEnumerator::idAt(int index)
{
return 0;
}
std::string AndroidEnumerator::nameAt(int index)
{
return "Audio";
}
void AndroidEnumerator::open(int direction)
{}
void AndroidEnumerator::close()
{}
// --------------- Input implementation ----------------
AndroidInputDevice::AndroidInputDevice(int devId)
{}
AndroidInputDevice::~AndroidInputDevice()
{
close();
}
bool AndroidInputDevice::open()
{
if (active())
return true;
oboe::AudioStreamBuilder builder;
builder.setDirection(oboe::Direction::Input);
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
builder.setSharingMode(oboe::SharingMode::Exclusive);
builder.setFormat(oboe::AudioFormat::I16);
builder.setChannelCount(oboe::ChannelCount::Mono);
builder.setCallback(this);
oboe::Result rescode = builder.openStream(&mRecordingStream);
if (rescode != oboe::Result::OK)
return false;
mDeviceRate = mRecordingStream->getSampleRate();
ICELogInfo(<< "Input Opened with rate " << mDeviceRate);
mActive = true;
rescode = mRecordingStream->requestStart();
if (rescode != oboe::Result::OK)
{
close();
mActive = false;
}
return mActive;
}
void AndroidInputDevice::close()
{
// There is no check for active() value because close() can be called to cleanup after bad open() call.
if (mRecordingStream != nullptr)
{
mRecordingStream->close();
delete mRecordingStream; mRecordingStream = nullptr;
}
mActive = false;
}
oboe::DataCallbackResult
AndroidInputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames)
{
std::unique_lock<std::mutex> l(mMutex);
// Send data to AudioPair
if (mConnection)
mConnection->onMicData(getFormat(), audioData, numFrames);
return oboe::DataCallbackResult::Continue;
}
Format AndroidInputDevice::getFormat()
{
return Format(mDeviceRate, 1);
}
bool AndroidInputDevice::active() const
{
return mActive;
}
bool AndroidInputDevice::fakeMode()
{
return false;
}
void AndroidInputDevice::setFakeMode(bool fakemode)
{}
int AndroidInputDevice::readBuffer(void* buffer)
{
throw std::runtime_error("AndroidInputDevice::readBuffer() is not implemented.");
}
// ------------ AndroidOutputDevice -----------------
AndroidOutputDevice::AndroidOutputDevice(int devId)
{
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
}
AndroidOutputDevice::~AndroidOutputDevice()
{
ICELogDebug(<< "Deleting AndroidOutputDevice.");
close();
}
bool AndroidOutputDevice::open()
{
std::unique_lock<std::mutex> l(mMutex);
if (mActive)
return true;
mRequestedFrames = 0;
mStartTime = 0.0;
mEndTime = 0.0;
oboe::AudioStreamBuilder builder;
builder.setDirection(oboe::Direction::Output);
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
builder.setSharingMode(oboe::SharingMode::Exclusive);
builder.setFormat(oboe::AudioFormat::I16);
builder.setChannelCount(oboe::ChannelCount::Mono);
// builder.setDataCallback(this);
builder.setCallback(this);
//builder.setErrorCallback(this)
oboe::Result rescode = builder.openStream(&mPlayingStream);
if (rescode != oboe::Result::OK)
return false;
mDeviceRate = mPlayingStream->getSampleRate();
ICELogInfo(<< "Input Opened with rate " << mDeviceRate);
mActive = true;
rescode = mPlayingStream->requestStart();
if (rescode != oboe::Result::OK)
{
close();
mActive = false;
}
return mActive;
}
void AndroidOutputDevice::close()
{
std::unique_lock<std::mutex> l(mMutex);
if (!mActive)
return;
if (mPlayingStream != nullptr)
{
mPlayingStream->close();
delete mPlayingStream; mPlayingStream = nullptr;
}
mEndTime = now_ms();
mActive = false;
ICELogInfo(<< "For time " << mEndTime - mStartTime << " ms was requested "
<< float(mRequestedFrames) / getFormat().mRate * 1000 << " ms");
}
Format AndroidOutputDevice::getFormat()
{
return {mDeviceRate, 1};
}
bool AndroidOutputDevice::fakeMode()
{
return false;
}
void AndroidOutputDevice::setFakeMode(bool /*fakemode*/)
{
}
oboe::DataCallbackResult AndroidOutputDevice::onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames)
{
if (mInShutdown)
return oboe::DataCallbackResult::Stop;
if (mStartTime == 0.0)
mStartTime = now_ms();
// Ask producer about data
memset(audioData, 0, numFrames * 2);
if (mConnection)
{
Format f = getFormat();
if (f.mRate != 0)
mConnection->onSpkData(f, audioData, numFrames * 2);
}
mRequestedFrames += numFrames;
return oboe::DataCallbackResult::Continue;
}
// TODO - special case https://github.com/google/oboe/blob/master/docs/notes/disconnect.md
void AndroidOutputDevice::onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result) {
if (result == oboe::Result::ErrorDisconnected) {
// LOGI("Restarting AudioStream after disconnect");
// soundEngine.restart(); // please check oboe samples for soundEngine.restart(); call
}
}
#endif // TARGET_ANDROID

View File

@@ -0,0 +1,109 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __AUDIO_ANDROID_OBOE_H
#define __AUDIO_ANDROID_OBOE_H
#ifdef TARGET_ANDROID
#include "Audio_Interface.h"
#include "Audio_Helper.h"
#include "Audio_Resampler.h"
#include "Audio_DataWindow.h"
#include "../helper/HL_Pointer.h"
#include "../helper/HL_ByteBuffer.h"
#include "../helper/HL_Exception.h"
#include "../helper/HL_Statistics.h"
#include <memory>
#include <string>
#include "oboe/Oboe.h"
namespace Audio
{
class AndroidEnumerator: public Enumerator
{
public:
AndroidEnumerator();
~AndroidEnumerator();
void open(int direction);
void close();
int count();
std::string nameAt(int index);
int idAt(int index);
int indexOfDefaultDevice();
protected:
};
class AndroidInputDevice: public InputDevice, public oboe::AudioStreamCallback
{
public:
AndroidInputDevice(int devId);
~AndroidInputDevice();
bool open();
void close();
Format getFormat();
bool fakeMode();
void setFakeMode(bool fakemode);
int readBuffer(void* buffer);
bool active() const;
oboe::DataCallbackResult
onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
protected:
bool mActive = false;
oboe::AudioStream* mRecordingStream = nullptr;
PResampler mResampler;
DataWindow mDeviceRateCache, mSdkRateCache;
int mDeviceRate; // Actual rate of opened recorder
int mBufferSize; // Size of buffer used for recording (at native sample rate)
DataWindow mRecorderBuffer;
std::condition_variable mDataCondVar;
int mRecorderBufferIndex;
std::mutex mMutex;
};
class AndroidOutputDevice: public OutputDevice, public oboe::AudioStreamCallback
{
public:
AndroidOutputDevice(int devId);
~AndroidOutputDevice();
bool open();
void close();
Format getFormat();
bool fakeMode();
void setFakeMode(bool fakemode);
oboe::DataCallbackResult onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames);
void onErrorAfterClose(oboe::AudioStream *stream, oboe::Result result);
protected:
std::mutex mMutex;
int mDeviceRate = 0;
oboe::AudioStream* mPlayingStream = nullptr;
DataWindow mPlayBuffer;
int mBufferIndex = 0, mBufferSize = 0;
bool mInShutdown = false;
bool mActive = false;
// Statistics
float mRequestedFrames = 0.0, mStartTime = 0.0, mEndTime = 0.0;
};
}
#endif // TARGET_ANDROID
#endif // __AUDIO_ANDROID_H

View File

@@ -56,17 +56,25 @@ void DataWindow::add(const void* data, int length)
if (length > mCapacity)
{
// Use latest bytes from data buffer in this case.
data = (char*)data + length - mCapacity;
length = mCapacity;
}
// Check how much free space we have
int avail = mCapacity - mFilled;
if (avail < length)
{
memmove(mData, mData + length - avail, mFilled - (length - avail));
mFilled -= length - avail;
// Find the portion of data to move & save
int delta = length - avail;
// Move the data
if (mFilled - delta > 0)
memmove(mData, mData + delta, mFilled - delta);
mFilled -= delta;
}
memcpy(mData + mFilled, data, length);
mFilled += length;
}

View File

@@ -197,7 +197,7 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
// Resample these 10 milliseconds it to native format
size_t wasProcessed = 0;
size_t wasProduced = mSpkResampler.resample(AUDIO_SAMPLERATE, mOutput10msBuffer.data(), mOutput10msBuffer.capacity(), wasProcessed, f.mRate,
size_t wasProduced = mSpkResampler.resample(nativeFormat.mRate, mOutput10msBuffer.data(), mOutput10msBuffer.capacity(), wasProcessed, f.mRate,
mOutputNativeData.mutableData() + mOutputNativeData.filled(), mOutputNativeData.capacity() - mOutputNativeData.filled());
mOutputNativeData.setFilled(mOutputNativeData.filled() + wasProduced);
#ifdef CONSOLE_LOGGING
@@ -206,7 +206,7 @@ void DevicePair::onSpkData(const Format& f, void* buffer, int length)
}
}
assert(mOutputNativeData.filled() >= length);
// assert(mOutputNativeData.filled() >= length);
#ifdef DUMP_NATIVEOUTPUT
if (mNativeOutputDump)
mNativeOutputDump->write(mOutputNativeData.data(), length);

View File

@@ -146,7 +146,8 @@ OsEngine* OsEngine::instance()
#endif
#ifdef TARGET_ANDROID
return &OpenSLEngine::instance();
return nullptr; // As we use Oboe library for now
//return &OpenSLEngine::instance();
#endif
return nullptr;

View File

@@ -216,48 +216,52 @@ void Mixer::mix()
channelList[activeCounter++] = &mChannelList[i];
// No active channels - nothing to mix - exit
if (!activeCounter)
if (!activeCounter)
{
//ICELogDebug(<< "No active channel");
// ICELogDebug(<< "No active channel");
return;
}
// Optimized versions for 1& 2 active channels
if (activeCounter == 1)
{
// Copy much samples as we have
{
// Copy much samples as we have
Stream& audio = *channelList[0];
mOutput.add(audio.data().data(), audio.data().filled());
audio.data().erase(audio.data().filled());
// Copy the decoded data
mOutput.add(audio.data().data(), audio.data().filled());
// Erase copied audio samples
audio.data().erase(audio.data().filled());
//ICELogSpecial(<<"Length of mixer stream " << audio.data().filled());
}
}
else
if (activeCounter == 2)
{
if (activeCounter == 2)
{
Stream& audio1 = *channelList[0];
Stream& audio2 = *channelList[1];
int filled1 = audio1.data().filled() / 2, filled2 = audio2.data().filled() / 2;
Stream& audio2 = *channelList[1];
int filled1 = audio1.data().filled() / 2, filled2 = audio2.data().filled() / 2;
int available = filled1 > filled2 ? filled1 : filled2;
// Find how much samples can be mixed
int filled = mOutput.filled() / 2;
// Find how much samples can be mixed
int filled = mOutput.filled() / 2;
int maxsize = mOutput.capacity() / 2;
if (maxsize - filled < available)
available = maxsize - filled;
if (maxsize - filled < available)
available = maxsize - filled;
short sample = 0;
for (int i=0; i<available; i++)
{
short sample = 0;
for (int i=0; i<available; i++)
{
short sample1 = filled1 > i ? audio1.data().shortAt(i) : 0;
short sample2 = filled2 > i ? audio2.data().shortAt(i) : 0;
sample = (abs(sample1) > abs(sample2)) ? sample1 : sample2;
mOutput.add(sample);
}
audio1.data().erase(available*2);
audio2.data().erase(available*2);
}
audio1.data().erase(available*2);
audio2.data().erase(available*2);
}
else
{
do

View File

@@ -1,14 +1,18 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
/* Copyright(C) 2007-2021 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Audio_Player.h"
#include "../helper/HL_Log.h"
#define LOG_SUBSYSTEM "Player"
using namespace Audio;
// -------------- Player -----------
Player::Player()
:mDelegate(NULL), mPlayedTime(0)
:mDelegate(nullptr), mPlayedTime(0)
{
}
@@ -47,7 +51,7 @@ void Player::onMicData(const Format& f, const void* buffer, int length)
void Player::onSpkData(const Format& f, void* buffer, int length)
{
Lock l(mGuard);
// Fill buffer by zero if player owns dedicated device
if (mOutput)
memset(buffer, 0, length);
@@ -99,7 +103,7 @@ void Player::onFilePlayed()
void Player::obtain(int usage)
{
Lock l(mGuard);
UsageMap::iterator usageIter = mUsage.find(usage);
auto usageIter = mUsage.find(usage);
if (usageIter == mUsage.end())
mUsage[usage] = 1;
else
@@ -132,7 +136,7 @@ int Player::releasePlayed()
{
Lock l(mGuard);
int result = mFinishedUsages.size();
while (mFinishedUsages.size())
while (!mFinishedUsages.empty())
{
release(mFinishedUsages.front());
mFinishedUsages.erase(mFinishedUsages.begin());

View File

@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com)
/* Copyright(C) 2007-2021 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -8,6 +8,7 @@
#include "../helper/HL_Log.h"
#include "../helper/HL_Sync.h"
#include "../helper/HL_Statistics.h"
#include "Audio_Interface.h"
#include <deque>
#include <map>
@@ -48,15 +49,18 @@ namespace Audio
void onMicData(const Format& f, const void* buffer, int length);
void onSpkData(const Format& f, void* buffer, int length);
void onFilePlayed();
void scheduleRelease();
void obtain(int usageId);
public:
Player();
~Player();
void setDelegate(EndOfAudioDelegate* d);
EndOfAudioDelegate* getDelegate() const;
void setOutput(POutputDevice output);
POutputDevice getOutput() const;
void add(int usageId, PWavFileReader file, bool loop, int timelength);
void release(int usageId);
void clear();

View File

@@ -17,7 +17,7 @@
#define AUDIO_CHANNELS 1
// Samplerate must be 8 / 16 / 24 / 32 / 48 KHz
#define AUDIO_SAMPLERATE 16000
#define AUDIO_SAMPLERATE 8000
#define AUDIO_MIC_BUFFER_COUNT 16
#define AUDIO_MIC_BUFFER_LENGTH 10
#define AUDIO_MIC_BUFFER_SIZE (AUDIO_MIC_BUFFER_LENGTH * AUDIO_SAMPLERATE / 1000 * 2 * AUDIO_CHANNELS)
@@ -50,7 +50,7 @@
#define MT_MAXRTPPACKET 1500
#define MT_DTMF_END_PACKETS 3
#define RTP_BUFFER_HIGH 480
#define RTP_BUFFER_HIGH 24480
#define RTP_BUFFER_LOW 10
#define RTP_BUFFER_PREBUFFER 80
#define RTP_DECODED_CAPACITY 2048
@@ -66,15 +66,15 @@
// AMR codec defines - it requires USE_AMR_CODEC defined
// #define USE_AMR_CODEC
#define MT_AMRNB_PAYLOADTYPE 122
#define MT_AMRNB_PAYLOADTYPE 112
#define MT_AMRNB_CODECNAME "amr"
#define MT_AMRNB_OCTET_PAYLOADTYPE 123
#define MT_AMRNB_OCTET_PAYLOADTYPE 113
#define MT_AMRWB_PAYLOADTYPE 124
#define MT_AMRWB_PAYLOADTYPE 96
#define MT_AMRWB_CODECNAME "amr-wb"
#define MT_AMRWB_OCTET_PAYLOADTYPE 125
#define MT_AMRWB_OCTET_PAYLOADTYPE 97
#define MT_GSMEFR_PAYLOADTYPE 126
#define MT_GSMEFR_CODECNAME "GERAN-EFR"

View File

@@ -28,19 +28,19 @@ public:
virtual ~AudioProvider();
// Returns provider RTP name
std::string streamName();
std::string streamName() override;
// Returns provider RTP profile name
std::string streamProfile();
std::string streamProfile() override;
// Sets destination IP address
void setDestinationAddress(const RtpPair<InternetAddress>& addr);
void setDestinationAddress(const RtpPair<InternetAddress>& addr) override;
// Processes incoming data
void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source);
void processData(PDatagramSocket s, const void* dataBuffer, int dataSize, InternetAddress& source) override;
// This method is called by user agent to send ICE packet from mediasocket
void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize);
void sendData(PDatagramSocket s, InternetAddress& destination, const void* dataBuffer, unsigned int datasize) override;
// Updates SDP offer
void updateSdpOffer(resip::SdpContents::Session::Medium& sdp, SdpDirection direction) override;

View File

@@ -93,7 +93,7 @@ void UserAgent::start()
}
// Initialize resip loggег
resip::Log::initialize(resip::Log::OnlyExternal, resip::Log::Info, "Client", *this);
resip::Log::initialize(resip::Log::OnlyExternal, resip::Log::Debug, "Client", *this);
// Build list of nameservers if specified
resip::DnsStub::NameserverList nslist;
@@ -151,7 +151,7 @@ void UserAgent::start()
switch (mConfig[CONFIG_TRANSPORT].asInt())
{
case 0:
case TransportType_Any:
if (mConfig[CONFIG_IPV4].asBool())
{
ADD_TRANSPORT4(resip::TCP)
@@ -166,21 +166,21 @@ void UserAgent::start()
}
break;
case 1:
case TransportType_Udp:
if (mConfig[CONFIG_IPV4].asBool())
ADD_TRANSPORT4(resip::UDP);
if (mConfig[CONFIG_IPV6].asBool())
ADD_TRANSPORT6(resip::UDP);
break;
case 2:
case TransportType_Tcp:
if (mConfig[CONFIG_IPV4].asBool())
ADD_TRANSPORT4(resip::TCP);
if (mConfig[CONFIG_IPV6].asBool())
ADD_TRANSPORT6(resip::TCP);
break;
case 3:
case TransportType_Tls:
if (mConfig[CONFIG_IPV4].asBool())
ADD_TRANSPORT4(resip::TLS);
if (mConfig[CONFIG_IPV6].asBool())

View File

@@ -60,6 +60,14 @@
#define RESIPROCATE_SUBSYSTEM Subsystem::TEST
using namespace std;
enum
{
TransportType_Any,
TransportType_Udp,
TransportType_Tcp,
TransportType_Tls
};
enum
{
CONFIG_IPV4 = 0, // Use IP4

View File

@@ -25,10 +25,15 @@ NetworkFrame::PacketData NetworkFrame::GetUdpPayloadForEthernet(NetworkFrame::Pa
uint16_t proto = 0;
if (ethernet->mEtherType == 129)
{
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(packet.mData);
packet.mData += sizeof(VlanHeader);
packet.mLength -= sizeof(VlanHeader);
proto = ntohs(vlan->mData);
// Skip 1 or more VLAN headers
do
{
const VlanHeader* vlan = reinterpret_cast<const VlanHeader*>(packet.mData);
packet.mData += sizeof(VlanHeader);
packet.mLength -= sizeof(VlanHeader);
proto = ntohs(vlan->mData);
}
while (proto == 0x8100);
}
// Skip MPLS headers

View File

@@ -245,6 +245,17 @@ std::string MediaStreamId::getFinishDescription() const
return oss.str();
}
MediaStreamId& MediaStreamId::operator = (const MediaStreamId& src)
{
this->mDestination = src.mDestination;
this->mSource = src.mSource;
this->mLinkId = src.mLinkId;
this->mSSRC = src.mSSRC;
this->mSsrcIsId = src.mSsrcIsId;
return *this;
}
std::ostream& operator << (std::ostream& output, const MediaStreamId& id)
{
return (output << id.toString());

View File

@@ -85,6 +85,7 @@ struct MediaStreamId
std::string toString() const;
std::string getDetectDescription() const;
std::string getFinishDescription() const;
MediaStreamId& operator = (const MediaStreamId& src);
};
std::ostream& operator << (std::ostream& output, const MediaStreamId& id);

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,82 @@
#ifndef __HELPER_STATISTICS_H
#define __HELPER_STATISTICS_H
template<typename T>
struct Average
{
int mCount = 0;
T mSum = 0;
T average() const
{
if (!mCount)
return 0;
return mSum / mCount;
}
T value() const
{
return average();
}
void process(T value)
{
mCount++;
mSum += value;
}
};
template<typename T, int minimum = 100000, int maximum = 0, int default_value = 0>
struct TestResult
{
T mMin = minimum;
T mMax = maximum;
Average<T> mAverage;
T mCurrent = default_value;
void process(T value)
{
if (mMin > value)
mMin = value;
if (mMax < value)
mMax = value;
mCurrent = value;
mAverage.process(value);
}
bool is_initialized() const
{
return mAverage.mCount > 0;
}
T current() const
{
if (is_initialized())
return mCurrent;
else
return 0;
}
T value() const
{
return current();
}
T average() const
{
return mAverage.average();
}
TestResult<T>& operator = (T value)
{
process(value);
return *this;
}
operator T()
{
return mCurrent;
}
};
#endif

View File

@@ -0,0 +1,10 @@
#include "HL_Time.h"
#include <time.h>
/* return current time in milliseconds */
double now_ms(void) {
struct timespec res;
clock_gettime(CLOCK_MONOTONIC, &res);
return 1000.0 * res.tv_sec + (double) res.tv_nsec / 1e6;
}

View File

@@ -0,0 +1,6 @@
#ifndef __HELPER_TIME_H
#define __HELPER_TIME_H
extern double now_ms();
#endif

View File

@@ -10,6 +10,7 @@
#include "MT_AudioCodec.h"
#include "MT_CngHelper.h"
#include "../helper/HL_Log.h"
#include "../helper/HL_Time.h"
#include "../audio/Audio_Interface.h"
#include "../audio/Audio_Resampler.h"
#include <cmath>
@@ -47,11 +48,19 @@ int RtpBuffer::Packet::rate() const
return mRate;
}
const std::vector<short>& RtpBuffer::Packet::pcm() const
{
return mPcm;
}
std::vector<short>& RtpBuffer::Packet::pcm()
{
return mPcm;
}
// ------------ RtpBuffer ----------------
RtpBuffer::RtpBuffer(Statistics& stat)
:mStat(stat), mSsrc(0), mHigh(RTP_BUFFER_HIGH), mLow(RTP_BUFFER_LOW), mPrebuffer(RTP_BUFFER_PREBUFFER),
mFirstPacketWillGo(true), mReturnedCounter(0), mAddCounter(0),
mFetchedPacket(std::shared_ptr<RTPPacket>(), 0, 0)
:mStat(stat)
{
}
@@ -96,19 +105,28 @@ int RtpBuffer::getCount() const
return static_cast<int>(mPacketList.size());
}
bool SequenceSort(const RtpBuffer::Packet& p1, const RtpBuffer::Packet& p2)
bool SequenceSort(const std::shared_ptr<RtpBuffer::Packet>& p1, const std::shared_ptr<RtpBuffer::Packet>& p2)
{
return p1.rtp()->GetExtendedSequenceNumber() < p2.rtp()->GetExtendedSequenceNumber();
return p1->rtp()->GetExtendedSequenceNumber() < p2->rtp()->GetExtendedSequenceNumber();
}
bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength, int rate)
std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength, int rate)
{
if (!packet)
return false;
return std::shared_ptr<Packet>();
Lock l(mGuard);
// Update statistics
if (mLastAddTime == 0.0)
mLastAddTime = now_ms();
else
{
float t = now_ms();
mStat.mPacketInterval.process(t - mLastAddTime);
mLastAddTime = t;
}
mStat.mSsrc = static_cast<uint16_t>(packet->GetSSRC());
// Update jitter
@@ -121,15 +139,15 @@ bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength,
// New sequence number
unsigned newSeqno = packet->GetExtendedSequenceNumber();
for (Packet& p: mPacketList)
for (std::shared_ptr<Packet>& p: mPacketList)
{
unsigned seqno = p.rtp()->GetExtendedSequenceNumber();
unsigned seqno = p->rtp()->GetExtendedSequenceNumber();
if (seqno == newSeqno)
{
mStat.mDuplicatedRtp++;
ICELogMedia(<< "Discovered duplicated packet, skipping");
return false;
return std::shared_ptr<Packet>();
}
if (seqno > maxno)
@@ -143,8 +161,11 @@ bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength,
if (newSeqno > minno || (available < mHigh))
{
Packet p(packet, timelength, rate);
// Insert into queue
auto p = std::make_shared<Packet>(packet, timelength, rate);
mPacketList.push_back(p);
// Sort again
std::sort(mPacketList.begin(), mPacketList.end(), SequenceSort);
// Limit by max timelength
@@ -152,21 +173,18 @@ bool RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, int timelength,
if (available > mHigh)
ICELogMedia(<< "Available " << available << "ms with limit " << mHigh << "ms");
/*while (available > mHigh && mPacketList.size())
{
ICELogDebug( << "Dropping RTP packet from jitter buffer");
available -= mPacketList.front().timelength();
mPacketList.erase(mPacketList.begin());
}*/
return p;
}
else
{
ICELogMedia(<< "Too old packet, skipping");
mStat.mOldRtp++;
return false;
return std::shared_ptr<Packet>();
}
return true;
return std::shared_ptr<Packet>();
}
RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
@@ -182,7 +200,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
while (total > mHigh && mPacketList.size())
{
ICELogMedia( << "Dropping RTP packets from jitter buffer");
total -= mPacketList.front().timelength();
total -= mPacketList.front()->timelength();
// Save it as last packet however - to not confuse loss packet counter
mFetchedPacket = mPacketList.front();
@@ -198,7 +216,11 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
result = FetchResult::NoPacket;
else
{
if (mFetchedPacket.rtp())
bool is_fetched_packet = mFetchedPacket.get() != nullptr;
if (is_fetched_packet)
is_fetched_packet &= mFetchedPacket->rtp().get() != nullptr;
if (is_fetched_packet)
{
if (mPacketList.empty())
{
@@ -208,10 +230,10 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
else
{
// Current sequence number ?
unsigned seqno = mPacketList.front().rtp()->GetExtendedSequenceNumber();
unsigned seqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber();
// Gap between new packet and previous on
int gap = seqno - mFetchedPacket.rtp()->GetSequenceNumber() - 1;
int gap = seqno - mFetchedPacket->rtp()->GetSequenceNumber() - 1;
gap = std::min(gap, 127);
if (gap > 0 && mPacketList.empty())
{
@@ -228,29 +250,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
}
result = FetchResult::RegularPacket;
Packet& p = mPacketList.front();
rl.push_back(p.rtp());
// Maybe it is time to replay packet right now ? For case of AMR SID packets
/*if (mFetchedPacket.rtp() && gap == 0 && mFetchedPacket.timelength() >= 10 && p.timelength() >= 10)
{
int timestampDelta;
// Timestamp difference
if (p.rtp()->GetTimestamp() > mFetchedPacket.rtp()->GetTimestamp())
timestampDelta = TimeHelper::getDelta(p.rtp()->GetTimestamp(), mFetchedPacket.rtp()->GetTimestamp());
else
timestampDelta = TimeHelper::getDelta(mFetchedPacket.rtp()->GetTimestamp(), p.rtp()->GetTimestamp());
// Timestamp units per packet
int nrOfPackets = timestampDelta / (p.timelength() * (p.rate() / 1000));
// Add more copies of SID (most probably) packets
for (int i = 0; i < nrOfPackets - 1; i++)
{
//assert(false);
rl.push_back(p.rtp());
}
}*/
rl.push_back(mPacketList.front());
// Save last returned normal packet
mFetchedPacket = mPacketList.front();
@@ -269,7 +269,7 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
result = FetchResult::RegularPacket;
// Put it to output list
rl.push_back(mPacketList.front().rtp());
rl.push_back(mPacketList.front());
// Remember returned packet
mFetchedPacket = mPacketList.front();
@@ -295,7 +295,7 @@ int RtpBuffer::findTimelength()
{
int available = 0;
for (unsigned i = 0; i < mPacketList.size(); i++)
available += mPacketList[i].timelength();
available += mPacketList[i]->timelength();
return available;
}
@@ -321,7 +321,7 @@ Receiver::~Receiver()
//-------------- AudioReceiver ----------------
AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat)
:Receiver(stat), mBuffer(stat), mFrameCount(0), mFailedCount(0), mCodecSettings(settings),
:Receiver(stat), mBuffer(stat), mCodecSettings(settings),
mCodecList(settings)
{
// Init resamplers
@@ -354,9 +354,45 @@ AudioReceiver::~AudioReceiver()
mDecodedDump.reset();
}
size_t decode_packet(Codec& codec, RTPPacket& p, void* output_buffer, size_t output_capacity)
{
// How much data was produced
size_t result = 0;
// Handle here regular RTP packets
// Check if payload length is ok
int tail = codec.rtpLength() ? p.GetPayloadLength() % codec.rtpLength() : 0;
if (!tail)
{
// Find number of frames
int frame_count = codec.rtpLength() ? p.GetPayloadLength() / codec.rtpLength() : 1;
int frame_length = codec.rtpLength() ? codec.rtpLength() : (int)p.GetPayloadLength();
// Save last packet time length
// mLastPacketTimeLength = mFrameCount * mCodec->frameTime();
// Decode
for (int i=0; i < frame_count; i++)
{
auto decoded_length = codec.decode(p.GetPayloadData() + i * codec.rtpLength(),
frame_length,
output_buffer,
output_capacity);
result += decoded_length;
}
}
else
ICELogMedia(<< "RTP packet with tail.");
return result;
}
bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec)
{
// ICELogInfo(<< "Adding packet No " << p->GetSequenceNumber());
// Increase codec counter
mStat.mCodecCount[p->GetPayloadType()]++;
@@ -410,7 +446,22 @@ bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** co
}
// Queue packet to buffer
return mBuffer.add(p, time_length, codecIter->second->samplerate());
auto packet = mBuffer.add(p, time_length, codecIter->second->samplerate()).get();
if (packet)
{
// Check if early decoding configured
if (mEarlyDecode && *codec)
{
// Move data to packet buffer
size_t available = decode_packet(**codec, *p, mDecodedFrame, sizeof mDecodedFrame);
packet->pcm().resize(available / 2);
memcpy(packet->pcm().data(), mDecodedFrame, available / 2);
}
return true;
}
else
return false;
}
void AudioReceiver::processDecoded(Audio::DataWindow& output, int options)
@@ -435,7 +486,7 @@ void AudioReceiver::processDecoded(Audio::DataWindow& output, int options)
bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
{
bool result = false;
bool result = false, /*had_cng = false, */had_decode = false;
// Get next packet from buffer
RtpBuffer::ResultList rl;
@@ -443,7 +494,7 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
switch (fr)
{
case RtpBuffer::FetchResult::Gap:
ICELogInfo(<< "Gap detected.");
ICELogDebug(<< "Gap detected.");
mDecodedLength = mResampledLength = 0;
if (mCngPacket && mCodec)
@@ -482,14 +533,14 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
case RtpBuffer::FetchResult::RegularPacket:
mFailedCount = 0;
for (std::shared_ptr<RTPPacket>& p: rl)
for (std::shared_ptr<RtpBuffer::Packet>& p: rl)
{
assert(p);
// Check if previously CNG packet was detected. Emit CNG audio here if needed.
if (options & DecodeOptions_FillCngGap && mCngPacket && mCodec)
{
// Fill CNG audio is server mode is present
int units = p->GetTimestamp() - mCngPacket->GetTimestamp();
int units = p->rtp()->GetTimestamp() - mCngPacket->GetTimestamp();
int milliseconds = units / (mCodec->samplerate() / 1000);
if (milliseconds > mLastPacketTimeLength)
{
@@ -522,69 +573,83 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
}
}
// Find codec
mCodec = mCodecMap[p->GetPayloadType()];
if (mCodec)
if (mEarlyDecode)
{
if (rate)
*rate = mCodec->samplerate();
// ToDo - copy the decoded data to output buffer
// Check if it is CNG packet
if ((p->GetPayloadType() == 0 || p->GetPayloadType() == 8) && p->GetPayloadLength() >= 1 && p->GetPayloadLength() <= 6)
}
else
{
// Find codec by payload type
int ptype = p->rtp()->GetPayloadType();
mCodec = mCodecMap[ptype];
if (mCodec)
{
if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0;
else
if (rate)
*rate = mCodec->samplerate();
// Check if it is CNG packet
if ((ptype == 0 || ptype == 8) && p->rtp()->GetPayloadLength() >= 1 && p->rtp()->GetPayloadLength() <= 6)
{
mCngPacket = p;
mCngDecoder.decode3389(p->GetPayloadData(), p->GetPayloadLength());
// Emit CNG mLastPacketLength milliseconds
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength,
(short*)mDecodedFrame, true);
if (mDecodedLength)
processDecoded(output, options);
}
result = true;
}
else
{
// Reset CNG packet
mCngPacket.reset();
// Handle here regular RTP packets
// Check if payload length is ok
int tail = mCodec->rtpLength() ? p->GetPayloadLength() % mCodec->rtpLength() : 0;
if (!tail)
{
// Find number of frames
mFrameCount = mCodec->rtpLength() ? p->GetPayloadLength() / mCodec->rtpLength() : 1;
int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)p->GetPayloadLength();
// Save last packet time length
mLastPacketTimeLength = mFrameCount * mCodec->frameTime();
// Decode
for (int i=0; i<mFrameCount && !mCodecSettings.mSkipDecode; i++)
if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0;
else
{
if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0;
else
{
// Decode frame by frame
mDecodedLength = mCodec->decode(p->GetPayloadData() + i*mCodec->rtpLength(),
frameLength, mDecodedFrame, sizeof mDecodedFrame);
if (mDecodedLength)
processDecoded(output, options);
}
mCngPacket = p->rtp();
mCngDecoder.decode3389(p->rtp()->GetPayloadData(), p->rtp()->GetPayloadLength());
// Emit CNG mLastPacketLength milliseconds
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength,
(short*)mDecodedFrame, true);
if (mDecodedLength)
processDecoded(output, options);
}
result = mFrameCount > 0;
// Check for bitrate counter
processStatisticsWithAmrCodec(mCodec.get());
result = true;
}
else
ICELogMedia(<< "RTP packet with tail.");
{
// Reset CNG packet
mCngPacket.reset();
// Handle here regular RTP packets
// Check if payload length is ok
int tail = mCodec->rtpLength() ? p->rtp()->GetPayloadLength() % mCodec->rtpLength() : 0;
if (!tail)
{
// Find number of frames
mFrameCount = mCodec->rtpLength() ? p->rtp()->GetPayloadLength() / mCodec->rtpLength() : 1;
int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)p->rtp()->GetPayloadLength();
// Save last packet time length
mLastPacketTimeLength = mFrameCount * mCodec->frameTime();
// Decode
for (int i=0; i<mFrameCount && !mCodecSettings.mSkipDecode; i++)
{
if (options & DecodeOptions_SkipDecode)
mDecodedLength = 0;
else
{
// Trigger the statistics
had_decode = true;
// Decode frame by frame
mDecodedLength = mCodec->decode(p->rtp()->GetPayloadData() + i * mCodec->rtpLength(),
frameLength, mDecodedFrame, sizeof mDecodedFrame);
// mDecodedLength = 3840; // Opus 20 ms stereo
if (mDecodedLength)
processDecoded(output, options);
}
}
result = mFrameCount > 0;
// Check for bitrate counter
processStatisticsWithAmrCodec(mCodec.get());
}
else
ICELogMedia(<< "RTP packet with tail.");
}
}
}
}
@@ -594,6 +659,18 @@ bool AudioReceiver::getAudio(Audio::DataWindow& output, int options, int* rate)
assert(0);
}
if (had_decode)
{
// mStat.mDecodeRequested++;
if (mLastDecodeTime == 0.0)
mLastDecodeTime = now_ms();
else
{
float t = now_ms();
mStat.mDecodingInterval.process(t - mLastDecodeTime);
mLastDecodeTime = t;
}
}
return result;
}
@@ -670,12 +747,12 @@ void AudioReceiver::updatePvqa(const void *data, int size)
mPvqaBuffer->addZero(size);
Audio::Format fmt;
int frames = (int)fmt.timeFromSize(mPvqaBuffer->filled()) / (PVQA_INTERVAL * 1000);
int frames = static_cast<int>(fmt.timeFromSize(mPvqaBuffer->filled())) / (PVQA_INTERVAL * 1000);
if (frames > 0)
{
int time4pvqa = (int)(frames * PVQA_INTERVAL * 1000);
int size4pvqa = (int)fmt.sizeFromTime(time4pvqa);
ICELogInfo(<< "PVQA buffer has " << time4pvqa << " milliseconds of audio.");
ICELogDebug(<< "PVQA buffer has " << time4pvqa << " milliseconds of audio.");
mPVQA->update(mPvqaBuffer->data(), size4pvqa);
mPvqaBuffer->erase(size4pvqa);
}

View File

@@ -47,12 +47,17 @@ namespace MT
public:
Packet(const std::shared_ptr<RTPPacket>& packet, int timelen, int rate);
std::shared_ptr<RTPPacket> rtp() const;
int timelength() const;
int rate() const;
const std::vector<short>& pcm() const;
std::vector<short>& pcm();
protected:
std::shared_ptr<RTPPacket> mRtp;
int mTimelength = 0, mRate = 0;
std::vector<short> mPcm;
};
RtpBuffer(Statistics& stat);
@@ -60,35 +65,48 @@ namespace MT
unsigned ssrc();
void setSsrc(unsigned ssrc);
void setHigh(int milliseconds);
int high();
void setLow(int milliseconds);
int low();
void setPrebuffer(int milliseconds);
int prebuffer();
int getNumberOfReturnedPackets() const;
int getNumberOfAddPackets() const;
int findTimelength();
int getCount() const;
// Returns false if packet was not add - maybe too old or too new or duplicate
bool add(std::shared_ptr<RTPPacket> packet, int timelength, int rate);
typedef std::vector<std::shared_ptr<RTPPacket>> ResultList;
// Returns false if packet was not add - maybe too old or too new or duplicate
std::shared_ptr<Packet> add(std::shared_ptr<RTPPacket> packet, int timelength, int rate);
typedef std::vector<std::shared_ptr<Packet>> ResultList;
typedef std::shared_ptr<ResultList> PResultList;
FetchResult fetch(ResultList& rl);
protected:
unsigned mSsrc;
int mHigh, mLow, mPrebuffer;
int mReturnedCounter, mAddCounter;
unsigned mSsrc = 0;
int mHigh = RTP_BUFFER_HIGH,
mLow = RTP_BUFFER_LOW,
mPrebuffer = RTP_BUFFER_PREBUFFER;
int mReturnedCounter = 0,
mAddCounter = 0;
mutable Mutex mGuard;
typedef std::vector<Packet> PacketList;
typedef std::vector<std::shared_ptr<Packet>> PacketList;
PacketList mPacketList;
Statistics& mStat;
bool mFirstPacketWillGo;
bool mFirstPacketWillGo = true;
jrtplib::RTPSourceStats mRtpStats;
Packet mFetchedPacket;
std::shared_ptr<Packet> mFetchedPacket;
// To calculate average interval between packet add. It is close to jitter but more useful in debugging.
float mLastAddTime = 0.0;
};
class Receiver
@@ -146,6 +164,9 @@ namespace MT
std::shared_ptr<jrtplib::RTPPacket> mCngPacket;
CngDecoder mCngDecoder;
// Decode RTP early, do not wait for speaker callback
bool mEarlyDecode = false;
// Buffer to hold decoded data
char mDecodedFrame[65536];
int mDecodedLength = 0;
@@ -161,12 +182,17 @@ namespace MT
// Last packet time length
int mLastPacketTimeLength = 0;
int mFailedCount;
int mFailedCount = 0;
Audio::Resampler mResampler8, mResampler16,
mResampler32, mResampler48;
Audio::PWavFileWriter mDecodedDump;
float mLastDecodeTime = 0.0; // Time last call happened to codec->decode()
float mIntervalSum = 0.0;
int mIntervalCount = 0;
// Zero rate will make audio mono but resampling will be skipped
void makeMonoAndResample(int rate, int channels);

View File

@@ -97,7 +97,7 @@ AudioStream::~AudioStream()
if (mFinalStatistics)
*mFinalStatistics = mStat;
ICELogInfo(<< mStat.toShortString());
ICELogInfo(<< mStat.toString());
}
void AudioStream::setDestination(const RtpPair<InternetAddress>& dest)

View File

@@ -34,7 +34,7 @@ Terminal::~Terminal()
mAudioPair.reset();
}
PStream Terminal::createStream(int type, VariantMap& config)
PStream Terminal::createStream(int type, VariantMap& /*config*/)
{
PStream result;
switch (type)
@@ -52,7 +52,7 @@ PStream Terminal::createStream(int type, VariantMap& config)
return result;
}
void Terminal::freeStream(PStream stream)
void Terminal::freeStream(const PStream& stream)
{
if (AudioStream* audio = dynamic_cast<AudioStream*>(stream.get()))
{

View File

@@ -26,10 +26,11 @@ namespace MT
CodecList& codeclist();
PStream createStream(int type, VariantMap& config);
void freeStream(PStream s);
void freeStream(const PStream& s);
Audio::PDevicePair audio();
void setAudio(const Audio::PDevicePair& audio);
protected:
StreamList mAudioList;
std::mutex mAudioListMutex;

View File

@@ -63,8 +63,13 @@ public:
virtual int channels() { return 1; }
// Returns size of encoded data (RTP) in bytes
virtual int encode(const void* input, int inputBytes, void* output, int outputCapacity) = 0;
// Returns size of decoded data (PCM signed short) in bytes
virtual int decode(const void* input, int inputBytes, void* output, int outputCapacity) = 0;
// Returns size of produced data (PCM signed short) in bytes
virtual int plc(int lostFrames, void* output, int outputCapacity) = 0;
// Returns size of codec in memory

View File

@@ -25,6 +25,42 @@ using namespace MT;
using strx = StringHelper;
// ---------------- EvsSpec ---------------
std::string CodecList::Settings::toString() const
{
std::ostringstream oss;
oss << "wrap IuUP: " << mWrapIuUP << std::endl
<< "skip decode: " << mSkipDecode << std::endl;
for (int ptype: mAmrWbPayloadType)
oss << "AMR WB ptype: " << ptype << std::endl;
for (int ptype: mAmrWbOctetPayloadType)
oss << "AMR WB octet-aligned ptype: " << ptype << std::endl;
for (int ptype: mAmrNbPayloadType)
oss << "AMR NB ptype: " << ptype << std::endl;
for (int ptype: mAmrNbOctetPayloadType)
oss << "AMR NB octet-aligned ptype:" << ptype << std::endl;
oss << "ISAC 16Khz ptype: " << mIsac16KPayloadType << std::endl
<< "ISAC 32Khz ptype: " << mIsac32KPayloadType << std::endl
<< "iLBC 20ms ptype: " << mIlbc20PayloadType << std::endl
<< "iLBC 30ms ptype: " << mIlbc30PayloadType << std::endl
<< "GSM FR ptype: " << mGsmFrPayloadType << ", GSM FR plength: " << mGsmFrPayloadLength << std::endl
<< "GSM HR ptype: " << mGsmHrPayloadType << std::endl
<< "GSM EFR ptype: " << mGsmEfrPayloadType << std::endl;
for (auto& spec: mEvsSpec)
{
oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << std::endl;
}
for (auto& spec: mOpusSpec)
{
oss << "OPUS ptype: " << spec.mPayloadType << ", rate: " << spec.mRate << ", channels: " << spec.mChannels << std::endl;
}
return oss.str();
}
bool CodecList::Settings::EvsSpec::isValid() const
{
return mPayloadType >= 96 && mPayloadType <= 127;
@@ -125,7 +161,6 @@ CodecList::CodecList(const Settings& settings)
mFactoryList.push_back(new GsmEfrCodec::GsmEfrFactory(mSettings.mWrapIuUP, mSettings.mGsmEfrPayloadType));
#endif
#endif
mFactoryList.push_back(new IsacCodec::IsacFactory16K(mSettings.mIsac16KPayloadType));
mFactoryList.push_back(new IlbcCodec::IlbcFactory(mSettings.mIlbc20PayloadType, mSettings.mIlbc30PayloadType));
mFactoryList.push_back(new G711Codec::AlawFactory());

View File

@@ -82,6 +82,9 @@ public:
};
std::vector<OpusSpec> mOpusSpec;
// Textual representation - used in logging
std::string toString() const;
static Settings DefaultSettings;
};

View File

@@ -19,6 +19,7 @@ SingleAudioStream::SingleAudioStream(const CodecList::Settings& codecSettings, S
SingleAudioStream::~SingleAudioStream()
{
}
void SingleAudioStream::process(const std::shared_ptr<jrtplib::RTPPacket>& packet)

View File

@@ -191,6 +191,9 @@ Statistics& Statistics::operator += (const Statistics& src)
mJitter = src.mJitter;
mRttDelay = src.mRttDelay;
mDecodingInterval = src.mDecodingInterval;
mDecodeRequested = src.mDecodeRequested;
if (!src.mCodecName.empty())
mCodecName = src.mCodecName;
@@ -239,13 +242,16 @@ Statistics& Statistics::operator -= (const Statistics& src)
}
std::string Statistics::toShortString() const
std::string Statistics::toString() const
{
std::ostringstream oss;
oss << "Received: " << mReceivedRtp
<< ", lost: " << mPacketLoss
<< ", dropped: " << mPacketDropped
<< ", sent: " << mSentRtp;
<< ", sent: " << mSentRtp
<< ", decoding interval: " << mDecodingInterval.average()
<< ", decode requested: " << mDecodeRequested.average()
<< ", packet interval: " << mPacketInterval.average();
return oss.str();
}

View File

@@ -6,6 +6,8 @@
#include "audio/Audio_DataWindow.h"
#include "helper/HL_Optional.hpp"
#include "helper/HL_Statistics.h"
#include "jrtplib/src/rtptimeutilities.h"
#include "jrtplib/src/rtppacket.h"
@@ -13,78 +15,6 @@ using std::experimental::optional;
namespace MT
{
template<typename T>
struct Average
{
int mCount = 0;
T mSum = 0;
T average() const
{
if (!mCount)
return 0;
return mSum / mCount;
}
T value() const
{
return average();
}
void process(T value)
{
mCount++;
mSum += value;
}
};
template<typename T, int minimum = 100000, int maximum = 0, int default_value = 0>
struct TestResult
{
T mMin = minimum;
T mMax = maximum;
Average<T> mAverage;
T mCurrent = default_value;
void process(T value)
{
if (mMin > value)
mMin = value;
if (mMax < value)
mMax = value;
mCurrent = value;
mAverage.process(value);
}
bool is_initialized() const
{
return mAverage.mCount > 0;
}
T current() const
{
if (is_initialized())
return mCurrent;
else
return 0;
}
T value() const
{
return current();
}
TestResult<T>& operator = (T value)
{
process(value);
return *this;
}
operator T()
{
return mCurrent;
}
};
template<typename T>
struct StreamStats
@@ -130,9 +60,13 @@ public:
mDuplicatedRtp, // Number of received duplicated rtp packets
mOldRtp, // Number of late rtp packets
mPacketLoss, // Number of lost packets
mPacketDropped, // Number of dropped packets (due to time unsync when playing)
mPacketDropped, // Number of dropped packets (due to time unsync when playing)б
mIllegalRtp; // Number of rtp packets with bad payload type
TestResult<float> mDecodingInterval, // Average interval on call to packet decode
mDecodeRequested, // Average amount of requested audio frames to play
mPacketInterval; // Average interval between packet adding to jitter buffer
int mLoss[128]; // Every item is number of loss of corresping length
size_t mAudioTime; // Decoded/found time in milliseconds
uint16_t mSsrc; // Last known SSRC ID in a RTP stream
@@ -169,7 +103,7 @@ public:
std::string mPvqaReport;
#endif
std::string toShortString() const;
std::string toString() const;
};
} // end of namespace MT

View File

@@ -21,7 +21,7 @@ Stream::Stream()
Stream::~Stream()
{
ICELogInfo(<< mStat.toString());
}
void Stream::setDestination(const RtpPair<InternetAddress>& dest)
@@ -77,13 +77,13 @@ StreamList::~StreamList()
clear();
}
void StreamList::add(PStream s)
void StreamList::add(const PStream& s)
{
Lock l(mMutex);
mStreamVector.push_back(s);
}
void StreamList::remove(PStream s)
void StreamList::remove(const PStream& s)
{
Lock l(mMutex);
@@ -98,7 +98,7 @@ void StreamList::clear()
mStreamVector.clear();
}
bool StreamList::has(PStream s)
bool StreamList::has(const PStream& s)
{
Lock l(mMutex);
return std::find(mStreamVector.begin(), mStreamVector.end(), s) != mStreamVector.end();
@@ -127,4 +127,4 @@ void StreamList::copyTo(StreamList* sl)
Mutex& StreamList::getMutex()
{
return mMutex;
}
}

View File

@@ -88,10 +88,10 @@ namespace MT
StreamList();
~StreamList();
void add(PStream s);
void remove(PStream s);
void add(const PStream& s);
void remove(const PStream& s);
void clear();
bool has(PStream s);
bool has(const PStream& s);
int size();
PStream streamAt(int index);