Merge remote-tracking branch 'origin/stable'
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
namespace Audio
|
||||
{
|
||||
|
||||
class AndroidEnumerator: public Enumerator
|
||||
{
|
||||
public:
|
||||
|
||||
245
src/engine/audio/Audio_AndroidOboe.cpp
Normal file
245
src/engine/audio/Audio_AndroidOboe.cpp
Normal 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
|
||||
109
src/engine/audio/Audio_AndroidOboe.h
Normal file
109
src/engine/audio/Audio_AndroidOboe.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
1
src/engine/helper/HL_Statistics.cpp
Normal file
1
src/engine/helper/HL_Statistics.cpp
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
82
src/engine/helper/HL_Statistics.h
Normal file
82
src/engine/helper/HL_Statistics.h
Normal 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
|
||||
10
src/engine/helper/HL_Time.cpp
Normal file
10
src/engine/helper/HL_Time.cpp
Normal 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;
|
||||
}
|
||||
6
src/engine/helper/HL_Time.h
Normal file
6
src/engine/helper/HL_Time.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef __HELPER_TIME_H
|
||||
#define __HELPER_TIME_H
|
||||
|
||||
extern double now_ms();
|
||||
|
||||
#endif
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ AudioStream::~AudioStream()
|
||||
if (mFinalStatistics)
|
||||
*mFinalStatistics = mStat;
|
||||
|
||||
ICELogInfo(<< mStat.toShortString());
|
||||
ICELogInfo(<< mStat.toString());
|
||||
}
|
||||
|
||||
void AudioStream::setDestination(const RtpPair<InternetAddress>& dest)
|
||||
|
||||
@@ -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()))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -82,6 +82,9 @@ public:
|
||||
};
|
||||
std::vector<OpusSpec> mOpusSpec;
|
||||
|
||||
// Textual representation - used in logging
|
||||
std::string toString() const;
|
||||
|
||||
static Settings DefaultSettings;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ SingleAudioStream::SingleAudioStream(const CodecList::Settings& codecSettings, S
|
||||
|
||||
SingleAudioStream::~SingleAudioStream()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SingleAudioStream::process(const std::shared_ptr<jrtplib::RTPPacket>& packet)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user