Compare commits

..

10 Commits

39 changed files with 3483 additions and 2753 deletions

View File

@@ -19,7 +19,8 @@ def make_build() -> Path:
os.mkdir(DIR_BUILD) os.mkdir(DIR_BUILD)
os.chdir(DIR_BUILD) os.chdir(DIR_BUILD)
cmd = f'cmake ../src -G Ninja' # OPUS_X86_MAY_HAVE_SSE4_1 is for clang builds
cmd = f'cmake ../src -G Ninja -D OPUS_X86_MAY_HAVE_SSE4_1=ON'
retcode = os.system(cmd) retcode = os.system(cmd)
if retcode != 0: if retcode != 0:
raise RuntimeError('Problem when configuring the project') raise RuntimeError('Problem when configuring the project')

View File

@@ -339,6 +339,8 @@ set (LIBS_STATIC ${LIBS_STATIC} jrtplib g729_codec gsm_codec opus
if (USE_AMR_CODEC) if (USE_AMR_CODEC)
#include (${LIB_PLATFORM}/platform_libs.cmake) #include (${LIB_PLATFORM}/platform_libs.cmake)
set (OPENCORE_AMRNB opencore-amrnb)
set (OPENCORE_AMRWB opencore-amrwb)
message("Media: AMR NB and WB codecs will be included.") message("Media: AMR NB and WB codecs will be included.")
set (DEFINES ${DEFINES} -DUSE_AMR_CODEC) set (DEFINES ${DEFINES} -DUSE_AMR_CODEC)
set (LIBS_STATIC ${LIBS_STATIC} ${OPENCORE_AMRNB} ${OPENCORE_AMRWB}) set (LIBS_STATIC ${LIBS_STATIC} ${OPENCORE_AMRNB} ${OPENCORE_AMRWB})

View File

@@ -16,6 +16,7 @@ const std::string Status_FailedToOpenFile = "failed to open file";
const std::string Status_NoActiveProvider = "no active provider"; const std::string Status_NoActiveProvider = "no active provider";
const std::string Status_NoMediaAction = "no valid media action"; const std::string Status_NoMediaAction = "no valid media action";
const std::string Status_NoCommand = "no valid command"; const std::string Status_NoCommand = "no valid command";
const std::string Status_NoAudioManager = "no audio manager";
#define LOG_SUBSYSTEM "Agent" #define LOG_SUBSYSTEM "Agent"
@@ -336,7 +337,7 @@ void AgentImpl::processStartSession(JsonCpp::Value& request, JsonCpp::Value& ans
{ {
// Agent was not started // Agent was not started
ICELogError(<< "No audio manager installed."); ICELogError(<< "No audio manager installed.");
answer["status"] = "Audio manager not started. Most probably agent is not started."; answer["status"] = Status_NoAudioManager;
return; return;
} }
@@ -431,28 +432,35 @@ void AgentImpl::processAcceptSession(JsonCpp::Value& request, JsonCpp::Value& an
auto sessionIter = mSessionMap.find(request["session_id"].asInt()); auto sessionIter = mSessionMap.find(request["session_id"].asInt());
if (sessionIter != mSessionMap.end()) if (sessionIter != mSessionMap.end())
{ {
// Ensure audio manager is here if (!mAudioManager)
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull); {
ICELogError(<< "No audio manager installed.");
answer["status"] = Status_NoAudioManager;
}
else
{
// Ensure audio manager is here
mAudioManager->start(mUseNativeAudio ? AudioManager::atReceiver : AudioManager::atNull);
// Accept session on SIP level // Accept session on SIP level
PSession session = sessionIter->second; PSession session = sessionIter->second;
// Get user headers // Get user headers
Session::UserHeaders info; Session::UserHeaders info;
JsonCpp::Value& arg = request["userinfo"]; JsonCpp::Value& arg = request["userinfo"];
std::vector<std::string> keys = arg.getMemberNames(); std::vector<std::string> keys = arg.getMemberNames();
for (const std::string& k: keys) for (const std::string& k: keys)
info[k] = arg[k].asString(); info[k] = arg[k].asString();
session->setUserHeaders(info); session->setUserHeaders(info);
// Accept finally // Accept finally
session->accept(); session->accept();
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
}
} }
else else
answer["status"] = Status_SessionNotFound; answer["status"] = Status_SessionNotFound;
} }
void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer) void AgentImpl::processDestroySession(JsonCpp::Value& request, JsonCpp::Value& answer)
@@ -526,6 +534,8 @@ void AgentImpl::processGetMediaStats(JsonCpp::Value& request, JsonCpp::Value& an
answer["rtt"] = result[SessionInfo_Rtt].asFloat(); answer["rtt"] = result[SessionInfo_Rtt].asFloat();
if (result.exists(SessionInfo_BitrateSwitchCounter)) if (result.exists(SessionInfo_BitrateSwitchCounter))
answer["bitrate_switch_counter"] = result[SessionInfo_BitrateSwitchCounter].asInt(); answer["bitrate_switch_counter"] = result[SessionInfo_BitrateSwitchCounter].asInt();
if (result.exists(SessionInfo_CngCounter))
answer["cng_counter"] = result[SessionInfo_CngCounter].asInt();
if (result.exists(SessionInfo_SSRC)) if (result.exists(SessionInfo_SSRC))
answer["rtp_ssrc"] = result[SessionInfo_SSRC].asInt(); answer["rtp_ssrc"] = result[SessionInfo_SSRC].asInt();
if (result.exists(SessionInfo_RemotePeer)) if (result.exists(SessionInfo_RemotePeer))
@@ -685,7 +695,7 @@ void AgentImpl::processUseStreamForSession(JsonCpp::Value& request, JsonCpp::Val
answer["status"] = Status_Ok; answer["status"] = Status_Ok;
} }
else else
answer["status"] = Status_AccountNotFound; answer["status"] = Status_NoCommand;
} }
else else
answer["status"] = Status_NoMediaAction; answer["status"] = Status_NoMediaAction;
@@ -707,11 +717,13 @@ void AgentImpl::onMedia(const void* data, int length, MT::Stream::MediaDirection
PDataProvider AgentImpl::onProviderNeeded(const std::string& name) PDataProvider AgentImpl::onProviderNeeded(const std::string& name)
{ {
assert(mTerminal);
EVENT_WITH_NAME("provider_needed"); EVENT_WITH_NAME("provider_needed");
v["provider_name"] = name; v["provider_name"] = name;
addEvent(v); addEvent(v);
return PDataProvider(new AudioProvider(*this, *mTerminal)); return std::make_shared<AudioProvider>(*this, *mTerminal);
} }
// Called on new session offer // Called on new session offer

View File

@@ -11,21 +11,31 @@ using namespace Audio;
DataWindow::DataWindow() DataWindow::DataWindow()
{ {
mFilled = 0; mFilled = 0;
mData = NULL; mData = nullptr;
mCapacity = 0; mCapacity = 0;
} }
DataWindow::~DataWindow() DataWindow::~DataWindow()
{ {
if (mData) if (mData)
{
free(mData); free(mData);
mData = nullptr;
}
} }
void DataWindow::setCapacity(int capacity) void DataWindow::setCapacity(int capacity)
{ {
Lock l(mMutex); Lock l(mMutex);
int tail = capacity - mCapacity; int tail = capacity - mCapacity;
char* buffer = mData;
mData = (char*)realloc(mData, capacity); mData = (char*)realloc(mData, capacity);
if (!mData)
{
// Realloc failed
mData = buffer;
throw std::bad_alloc();
}
if (tail > 0) if (tail > 0)
memset(mData + mCapacity, 0, tail); memset(mData + mCapacity, 0, tail);
mCapacity = capacity; mCapacity = capacity;
@@ -166,6 +176,25 @@ void DataWindow::zero(int length)
memset(mData, 0, mFilled); memset(mData, 0, mFilled);
} }
size_t DataWindow::moveTo(DataWindow& dst, size_t size)
{
Lock l(mMutex);
size_t avail = std::min(size, (size_t)filled());
if (avail != 0)
{
dst.add(mData, avail);
erase(avail);
}
return avail;
}
std::chrono::milliseconds DataWindow::getTimeLength(int samplerate, int channels) const
{
Lock l(mMutex);
return std::chrono::milliseconds(mFilled / sizeof(short) / channels / (samplerate / 1000));
}
void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src) void DataWindow::makeStereoFromMono(DataWindow& dst, DataWindow& src)
{ {
Lock lockDst(dst.mMutex), lockSrc(src.mMutex); Lock lockDst(dst.mMutex), lockSrc(src.mMutex);

View File

@@ -11,37 +11,40 @@
namespace Audio namespace Audio
{ {
class DataWindow class DataWindow
{ {
public: public:
DataWindow(); DataWindow();
~DataWindow(); ~DataWindow();
void setCapacity(int capacity); void setCapacity(int capacity);
int capacity() const; int capacity() const;
void addZero(int length); void addZero(int length);
void add(const void* data, int length); void add(const void* data, int length);
void add(short sample); void add(short sample);
int read(void* buffer, int length); int read(void* buffer, int length);
void erase(int length = -1); void erase(int length = -1);
const char* data() const; const char* data() const;
char* mutableData(); char* mutableData();
int filled() const; int filled() const;
void setFilled(int filled); void setFilled(int filled);
void clear(); void clear();
short shortAt(int index) const; short shortAt(int index) const;
void setShortAt(short value, int index); void setShortAt(short value, int index);
void zero(int length); void zero(int length);
size_t moveTo(DataWindow& dst, size_t size);
std::chrono::milliseconds getTimeLength(int samplerate, int channels) const;
static void makeStereoFromMono(DataWindow& dst, DataWindow& src); static void makeStereoFromMono(DataWindow& dst, DataWindow& src);
protected: protected:
mutable Mutex mMutex; mutable Mutex mMutex;
char* mData; char* mData;
int mFilled; int mFilled;
int mCapacity; int mCapacity;
}; };
} }
#endif #endif

View File

@@ -51,6 +51,11 @@ struct Format
return float((milliseconds * mRate) / 500.0 * mChannels); return float((milliseconds * mRate) / 500.0 * mChannels);
} }
size_t sizeFromTime(std::chrono::milliseconds ms) const
{
return sizeFromTime(ms.count());
}
std::string toString() std::string toString()
{ {
char buffer[64]; char buffer[64];

View File

@@ -483,6 +483,7 @@ void Session::getSessionInfo(Session::InfoOptions options, VariantMap& info)
info[SessionInfo_Rtt] = static_cast<float>(stat.mRttDelay * 1000); info[SessionInfo_Rtt] = static_cast<float>(stat.mRttDelay * 1000);
#if defined(USE_AMR_CODEC) #if defined(USE_AMR_CODEC)
info[SessionInfo_BitrateSwitchCounter] = stat.mBitrateSwitchCounter; info[SessionInfo_BitrateSwitchCounter] = stat.mBitrateSwitchCounter;
info[SessionInfo_CngCounter] = stat.mCng;
#endif #endif
info[SessionInfo_SSRC] = stat.mSsrc; info[SessionInfo_SSRC] = stat.mSsrc;
info[SessionInfo_RemotePeer] = stat.mRemotePeer.toStdString(); info[SessionInfo_RemotePeer] = stat.mRemotePeer.toStdString();
@@ -882,7 +883,7 @@ void Session::refreshMediaPath()
// Bring new socket to provider and stream // Bring new socket to provider and stream
RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX() ), RtpPair<PDatagramSocket> s4 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX() ),
s6 = SocketHeap::instance().allocSocketPair(AF_INET, this, IS_MULTIPLEX()); s6 = SocketHeap::instance().allocSocketPair(AF_INET6, this, IS_MULTIPLEX());
p->setSocket(s4, s6); p->setSocket(s4, s6);
s.setSocket4(s4); s.setSocket4(s4);

View File

@@ -72,6 +72,7 @@ enum SessionInfo
SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only SessionInfo_BitrateSwitchCounter, // It is for AMR codecs only
SessionInfo_RemotePeer, SessionInfo_RemotePeer,
SessionInfo_SSRC, SessionInfo_SSRC,
SessionInfo_CngCounter // For AMR codecs only
}; };

View File

@@ -38,11 +38,11 @@
//#define AUDIO_DUMPOUTPUT //#define AUDIO_DUMPOUTPUT
#define UA_REGISTRATION_TIME 3600 #define UA_REGISTRATION_TIME 3600
#define UA_MEDIA_PORT_START 20000 #define UA_MEDIA_PORT_START 20000
#define UA_MEDIA_PORT_FINISH 30000 #define UA_MEDIA_PORT_FINISH 30000
#define UA_MAX_UDP_PACKET_SIZE 576 #define UA_MAX_UDP_PACKET_SIZE 576
#define UA_PUBLICATION_ID "314" #define UA_PUBLICATION_ID "314"
#define MT_SAMPLERATE AUDIO_SAMPLERATE #define MT_SAMPLERATE AUDIO_SAMPLERATE
@@ -50,13 +50,11 @@
#define MT_MAXRTPPACKET 1500 #define MT_MAXRTPPACKET 1500
#define MT_DTMF_END_PACKETS 3 #define MT_DTMF_END_PACKETS 3
#define RTP_BUFFER_HIGH 0 // Milliseconds before
#define RTP_BUFFER_LOW 0 #define RTP_BUFFER_HIGH (2000)
#define RTP_BUFFER_PREBUFFER 0 #define RTP_BUFFER_LOW (0)
#define RTP_BUFFER_PREBUFFER (100)
// #define RTP_BUFFER_HIGH 160
// #define RTP_BUFFER_LOW 10
// #define RTP_BUFFER_PREBUFFER 160
#define RTP_DECODED_CAPACITY 2048 #define RTP_DECODED_CAPACITY 2048
#define DEFAULT_SUBSCRIPTION_TIME 1200 #define DEFAULT_SUBSCRIPTION_TIME 1200

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2017 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2026 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -9,6 +9,7 @@
#include "../engine_config.h" #include "../engine_config.h"
#include "HL_NetworkSocket.h" #include "HL_NetworkSocket.h"
#include "HL_Log.h"
#if defined(TARGET_OSX) || defined(TARGET_LINUX) #if defined(TARGET_OSX) || defined(TARGET_LINUX)
# include <fcntl.h> # include <fcntl.h>
@@ -19,11 +20,11 @@
#endif #endif
#include <assert.h> #include <assert.h>
#define LOG_SUBSYSTEM "network"
DatagramSocket::DatagramSocket() DatagramSocket::DatagramSocket()
:mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0) :mFamily(AF_INET), mHandle(INVALID_SOCKET), mLocalPort(0)
{ {}
}
DatagramSocket::~DatagramSocket() DatagramSocket::~DatagramSocket()
{ {
@@ -160,6 +161,12 @@ void DatagramAgreggator::addSocket(PDatagramSocket socket)
if (socket->mHandle == INVALID_SOCKET) if (socket->mHandle == INVALID_SOCKET)
return; return;
if (mSocketVector.size() >= 62)
{
ICELogError(<< "fd_set overflow; too much sockets");
return;
}
FD_SET(socket->mHandle, &mReadSet); FD_SET(socket->mHandle, &mReadSet);
if (socket->mHandle > mMaxHandle) if (socket->mHandle > mMaxHandle)
mMaxHandle = socket->mHandle; mMaxHandle = socket->mHandle;

View File

@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com) /* Copyright(C) 2007-2026 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -8,70 +8,95 @@
# include <Windows.h> # include <Windows.h>
#endif #endif
#if defined(TARGET_LINUX) || defined(TARGET_ANDROID) #if defined(TARGET_LINUX) || defined(TARGET_ANDROID) || defined(TARGET_OSX)
# include <arpa/inet.h> # include <arpa/inet.h>
#endif #endif
#include "HL_Rtp.h" #include "HL_Rtp.h"
#include "HL_Exception.h" #include "HL_Exception.h"
#include "HL_String.h" #include "HL_Log.h"
#if defined(USE_RTP_DUMP) #include "jrtplib/src/rtprawpacket.h"
# include "jrtplib/src/rtprawpacket.h" #include "jrtplib/src/rtpipv4address.h"
# include "jrtplib/src/rtpipv4address.h"
#endif
#if !defined(TARGET_WIN) #include <stdexcept>
# include <alloca.h> #include <fstream>
#endif #include <cstring>
#include <cstdio>
#include <chrono>
#include <sstream> #define LOG_SUBSYSTEM "RtpDump"
#include <tuple>
static constexpr size_t MAX_RTP_PACKET_SIZE = 65535;
static const char RTPDUMP_SHEBANG[] = "#!rtpplay1.0";
// RTP fixed header (little-endian bit-field layout)
struct RtpHeader struct RtpHeader
{ {
unsigned char cc:4; /* CSRC count */ unsigned char cc:4; /* CSRC count */
unsigned char x:1; /* header extension flag */ unsigned char x:1; /* header extension flag */
unsigned char p:1; /* padding flag */ unsigned char p:1; /* padding flag */
unsigned char version:2; /* protocol version */ unsigned char version:2; /* protocol version */
unsigned char pt:7; /* payload type */ unsigned char pt:7; /* payload type */
unsigned char m:1; /* marker bit */ unsigned char m:1; /* marker bit */
unsigned short seq; /* sequence number */ unsigned short seq; /* sequence number */
unsigned int ts; /* timestamp */ unsigned int ts; /* timestamp */
unsigned int ssrc; /* synchronization source */ unsigned int ssrc; /* synchronization source */
}; };
struct RtcpHeader struct RtcpHeader
{ {
unsigned char rc:5; /* reception report count */ unsigned char rc:5; /* reception report count */
unsigned char p:1; /* padding flag */ unsigned char p:1; /* padding flag */
unsigned char version:2; /* protocol version */ unsigned char version:2; /* protocol version */
unsigned char pt:8; /* payload type */ unsigned char pt; /* payload type */
uint16_t len; /* length */ uint16_t len; /* length */
uint32_t ssrc; /* synchronization source */ uint32_t ssrc; /* synchronization source */
}; };
// --- IPv4 address helpers ---
static std::string ipToString(uint32_t ip)
{
// ip in host byte order → dotted-decimal
return std::to_string((ip >> 24) & 0xFF) + "." +
std::to_string((ip >> 16) & 0xFF) + "." +
std::to_string((ip >> 8) & 0xFF) + "." +
std::to_string( ip & 0xFF);
}
static uint32_t stringToIp(const std::string& s)
{
unsigned a = 0, b = 0, c = 0, d = 0;
if (std::sscanf(s.c_str(), "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
return 0;
if (a > 255 || b > 255 || c > 255 || d > 255)
return 0;
return (a << 24) | (b << 16) | (c << 8) | d;
}
// --- RtpHelper implementation ---
bool RtpHelper::isRtp(const void* buffer, size_t length) bool RtpHelper::isRtp(const void* buffer, size_t length)
{ {
if (length < 12) if (length < 12)
return false; return false;
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer); const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
if (h->version != 0b10) if (h->version != 2)
return false; return false;
unsigned char pt = h->pt; unsigned char pt = h->pt;
bool rtp = ( (pt & 0x7F) >= 96 && (pt & 0x7F) <= 127) || ((pt & 0x7F) < 35); bool rtp = (pt >= 96 && pt <= 127) || (pt < 35);
return rtp; return rtp;
} }
bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length) bool RtpHelper::isRtpOrRtcp(const void* buffer, size_t length)
{ {
if (length < 12) if (length < 12)
return false; return false;
const RtcpHeader* h = reinterpret_cast<const RtcpHeader*>(buffer); const RtcpHeader* h = reinterpret_cast<const RtcpHeader*>(buffer);
return h->version == 0b10; return h->version == 2;
} }
bool RtpHelper::isRtcp(const void* buffer, size_t length) bool RtpHelper::isRtcp(const void* buffer, size_t length)
@@ -83,15 +108,16 @@ unsigned RtpHelper::findSsrc(const void* buffer, size_t length)
{ {
if (isRtp(buffer, length)) if (isRtp(buffer, length))
return ntohl(reinterpret_cast<const RtpHeader*>(buffer)->ssrc); return ntohl(reinterpret_cast<const RtpHeader*>(buffer)->ssrc);
else else if (isRtpOrRtcp(buffer, length))
return ntohl(reinterpret_cast<const RtcpHeader*>(buffer)->ssrc); return ntohl(reinterpret_cast<const RtcpHeader*>(buffer)->ssrc);
return 0;
} }
void RtpHelper::setSsrc(void* buffer, size_t length, uint32_t ssrc) void RtpHelper::setSsrc(void* buffer, size_t length, uint32_t ssrc)
{ {
if (isRtp(buffer, length)) if (isRtp(buffer, length))
reinterpret_cast<RtpHeader*>(buffer)->ssrc = htonl(ssrc); reinterpret_cast<RtpHeader*>(buffer)->ssrc = htonl(ssrc);
else else if (isRtpOrRtcp(buffer, length))
reinterpret_cast<RtcpHeader*>(buffer)->ssrc = htonl(ssrc); reinterpret_cast<RtcpHeader*>(buffer)->ssrc = htonl(ssrc);
} }
@@ -113,47 +139,191 @@ int RtpHelper::findPacketNo(const void *buffer, size_t length)
int RtpHelper::findPayloadLength(const void* buffer, size_t length) int RtpHelper::findPayloadLength(const void* buffer, size_t length)
{ {
if (isRtp(buffer, length)) if (!isRtp(buffer, length))
{
return length - 12;
}
else
return -1; return -1;
const RtpHeader* h = reinterpret_cast<const RtpHeader*>(buffer);
const uint8_t* p = static_cast<const uint8_t*>(buffer);
// Fixed header (12 bytes) + CSRC list (4 * CC bytes)
size_t offset = 12 + 4u * h->cc;
if (offset > length)
return -1;
// Header extension
if (h->x) {
if (offset + 4 > length)
return -1;
uint16_t extWords = (static_cast<uint16_t>(p[offset + 2]) << 8) | p[offset + 3];
offset += 4 + 4u * extWords;
if (offset > length)
return -1;
}
size_t payloadLen = length - offset;
// Padding
if (h->p && payloadLen > 0) {
uint8_t padBytes = p[length - 1];
if (padBytes > payloadLen)
return -1;
payloadLen -= padBytes;
}
return static_cast<int>(payloadLen);
} }
#if defined(USE_RTPDUMP) std::chrono::microseconds RtpHelper::toMicroseconds(const jrtplib::RTPTime& t)
RtpDump::RtpDump(const char *filename)
:mFilename(filename)
{}
RtpDump::~RtpDump()
{ {
flush(); return std::chrono::microseconds(uint64_t(t.GetDouble() * 1000000));
for (PacketList::iterator packetIter=mPacketList.begin(); packetIter!=mPacketList.end(); ++packetIter) }
{
//free(packetIter->mData); // --- RtpDump implementation ---
delete packetIter->mPacket;
std::shared_ptr<jrtplib::RTPPacket> RtpDump::parseRtpData(const uint8_t* data, size_t len)
{
if (!data || len < 12 || !RtpHelper::isRtp(data, len))
return nullptr;
try {
// Both are heap-allocated; RTPRawPacket takes ownership and deletes them
auto* addr = new jrtplib::RTPIPv4Address(uint32_t(0), uint16_t(0));
uint8_t* dataCopy = new uint8_t[len];
std::memcpy(dataCopy, data, len);
jrtplib::RTPRawPacket raw(dataCopy, len, addr, jrtplib::RTPTime(0), true);
auto packet = std::make_shared<jrtplib::RTPPacket>(raw);
if (packet->GetCreationError() != 0)
return nullptr;
return packet;
} catch (const std::exception& e) {
ICELogInfo(<< "Failed to parse RTP packet: " << e.what());
return nullptr;
} }
} }
RtpDump::RtpDump(const char* filename)
: mFilename(filename ? filename : "")
{
}
RtpDump::~RtpDump() = default;
void RtpDump::setSource(uint32_t ip, uint16_t port)
{
mSourceIp = ip;
mSourcePort = port;
}
void RtpDump::load() void RtpDump::load()
{ {
FILE* f = fopen(mFilename.c_str(), "rb"); if (mFilename.empty())
if (!f) throw std::runtime_error("No filename specified");
throw Exception(ERR_WAVFILE_FAILED);
while (!feof(f)) std::ifstream input(mFilename, std::ios::binary);
{ if (!input.is_open())
RtpData data; throw std::runtime_error("Failed to open RTP dump file: " + mFilename);
fread(&data.mLength, sizeof data.mLength, 1, f);
data.mData = new char[data.mLength]; mPacketList.clear();
fread(data.mData, 1, data.mLength, f);
jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address); // --- 1. Text header: "#!rtpplay1.0 <ip>/<port>\n" ---
jrtplib::RTPTime t(0); std::string textLine;
jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)data.mData, data.mLength, &addr, t, true); std::getline(input, textLine);
data.mPacket = new jrtplib::RTPPacket(*raw); if (textLine.compare(0, sizeof(RTPDUMP_SHEBANG) - 1, RTPDUMP_SHEBANG) != 0)
mPacketList.push_back(data); throw std::runtime_error("Invalid rtpdump header: expected " + std::string(RTPDUMP_SHEBANG));
// Parse source address from the text line
size_t spacePos = textLine.find(' ');
if (spacePos != std::string::npos) {
std::string addrPart = textLine.substr(spacePos + 1);
size_t slashPos = addrPart.find('/');
if (slashPos != std::string::npos) {
mSourceIp = stringToIp(addrPart.substr(0, slashPos));
try {
mSourcePort = static_cast<uint16_t>(std::stoi(addrPart.substr(slashPos + 1)));
} catch (...) {
mSourcePort = 0;
}
}
} }
// --- 2. Binary file header (RD_hdr_t, 16 bytes) ---
uint32_t buf32;
uint16_t buf16;
input.read(reinterpret_cast<char*>(&buf32), 4);
mStartSec = ntohl(buf32);
input.read(reinterpret_cast<char*>(&buf32), 4);
mStartUsec = ntohl(buf32);
input.read(reinterpret_cast<char*>(&buf32), 4); // source IP (already NBO in file)
// The binary header stores IP in network byte order; convert to host
mSourceIp = ntohl(buf32);
input.read(reinterpret_cast<char*>(&buf16), 2);
mSourcePort = ntohs(buf16);
input.read(reinterpret_cast<char*>(&buf16), 2); // padding — discard
if (!input.good())
throw std::runtime_error("Failed to read rtpdump binary header");
// --- 3. Packet records ---
size_t packetCount = 0;
while (input.good() && input.peek() != EOF) {
// Packet header: length(2) + plen(2) + offset(4) = 8 bytes
uint16_t recLength, plen;
uint32_t offsetMs;
input.read(reinterpret_cast<char*>(&recLength), 2);
if (input.gcount() != 2) break;
recLength = ntohs(recLength);
input.read(reinterpret_cast<char*>(&plen), 2);
if (input.gcount() != 2) break;
plen = ntohs(plen);
input.read(reinterpret_cast<char*>(&offsetMs), 4);
if (input.gcount() != 4) break;
offsetMs = ntohl(offsetMs);
// All-zeros record signals end of file in some implementations
if (recLength == 0 && plen == 0 && offsetMs == 0)
break;
if (plen == 0 || plen > MAX_RTP_PACKET_SIZE)
throw std::runtime_error("Invalid packet payload length: " + std::to_string(plen));
if (recLength < plen + 8)
throw std::runtime_error("Record length (" + std::to_string(recLength) +
") smaller than payload + header (" + std::to_string(plen + 8) + ")");
// Read body
std::vector<uint8_t> body(plen);
input.read(reinterpret_cast<char*>(body.data()), plen);
if (static_cast<size_t>(input.gcount()) != plen)
throw std::runtime_error("Incomplete packet data in rtpdump file");
// Skip any padding between plen and recLength-8
size_t pad = static_cast<size_t>(recLength) - 8 - plen;
if (pad > 0)
input.seekg(static_cast<std::streamoff>(pad), std::ios::cur);
RtpData entry;
entry.mRawData = std::move(body);
entry.mOffsetMs = offsetMs;
entry.mPacket = parseRtpData(entry.mRawData.data(), entry.mRawData.size());
mPacketList.push_back(std::move(entry));
packetCount++;
}
ICELogInfo(<< "Loaded " << packetCount << " packets from " << mFilename);
mLoaded = true;
} }
size_t RtpDump::count() const size_t RtpDump::count() const
@@ -163,39 +333,142 @@ size_t RtpDump::count() const
jrtplib::RTPPacket& RtpDump::packetAt(size_t index) jrtplib::RTPPacket& RtpDump::packetAt(size_t index)
{ {
if (index >= mPacketList.size())
throw std::out_of_range("Packet index out of range: " + std::to_string(index));
if (!mPacketList[index].mPacket)
throw std::runtime_error("No parsed RTP data at index " + std::to_string(index));
return *mPacketList[index].mPacket; return *mPacketList[index].mPacket;
} }
const std::vector<uint8_t>& RtpDump::rawDataAt(size_t index) const
{
if (index >= mPacketList.size())
throw std::out_of_range("Packet index out of range: " + std::to_string(index));
return mPacketList[index].mRawData;
}
uint32_t RtpDump::offsetAt(size_t index) const
{
if (index >= mPacketList.size())
throw std::out_of_range("Packet index out of range: " + std::to_string(index));
return mPacketList[index].mOffsetMs;
}
void RtpDump::add(const void* buffer, size_t len) void RtpDump::add(const void* buffer, size_t len)
{ {
RtpData data; if (!buffer || len == 0)
data.mData = malloc(len); return;
memcpy(data.mData, buffer, len);
data.mLength = len;
jrtplib::RTPIPv4Address addr(jrtplib::RTPAddress::IPv4Address); uint32_t offsetMs = 0;
jrtplib::RTPTime t(0); auto now = std::chrono::steady_clock::now();
jrtplib::RTPRawPacket* raw = new jrtplib::RTPRawPacket((unsigned char*)const_cast<void*>(data.mData), data.mLength, &addr, t, true);
data.mPacket = new jrtplib::RTPPacket(*raw); if (!mRecording) {
//delete raw; mRecording = true;
mPacketList.push_back(data); mRecordStart = now;
// Capture wall-clock start time
auto wallNow = std::chrono::system_clock::now();
auto epoch = wallNow.time_since_epoch();
auto sec = std::chrono::duration_cast<std::chrono::seconds>(epoch);
auto usec = std::chrono::duration_cast<std::chrono::microseconds>(epoch - sec);
mStartSec = static_cast<uint32_t>(sec.count());
mStartUsec = static_cast<uint32_t>(usec.count());
} else {
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - mRecordStart);
offsetMs = static_cast<uint32_t>(elapsed.count());
}
add(buffer, len, offsetMs);
}
void RtpDump::add(const void* buffer, size_t len, uint32_t offsetMs)
{
if (!buffer || len == 0)
return;
if (len > MAX_RTP_PACKET_SIZE)
throw std::runtime_error("Packet too large: " + std::to_string(len));
RtpData entry;
entry.mRawData.assign(static_cast<const uint8_t*>(buffer),
static_cast<const uint8_t*>(buffer) + len);
entry.mOffsetMs = offsetMs;
entry.mPacket = parseRtpData(entry.mRawData.data(), entry.mRawData.size());
mPacketList.push_back(std::move(entry));
} }
void RtpDump::flush() void RtpDump::flush()
{ {
FILE* f = fopen(mFilename.c_str(), "wb"); if (mFilename.empty())
if (!f) throw std::runtime_error("No filename specified");
throw Exception(ERR_WAVFILE_FAILED);
PacketList::iterator packetIter = mPacketList.begin(); std::ofstream output(mFilename, std::ios::binary);
for (;packetIter != mPacketList.end(); ++packetIter) if (!output.is_open())
{ throw std::runtime_error("Failed to open file for writing: " + mFilename);
RtpData& data = *packetIter;
// Disabled for debugging only // --- 1. Text header ---
//fwrite(&data.mLength, sizeof data.mLength, 1, f); std::string textLine = std::string(RTPDUMP_SHEBANG) + " " +
fwrite(data.mData, data.mLength, 1, f); ipToString(mSourceIp) + "/" +
std::to_string(mSourcePort) + "\n";
output.write(textLine.data(), static_cast<std::streamsize>(textLine.size()));
// --- 2. Binary file header (16 bytes) ---
uint32_t buf32;
uint16_t buf16;
buf32 = htonl(mStartSec);
output.write(reinterpret_cast<const char*>(&buf32), 4);
buf32 = htonl(mStartUsec);
output.write(reinterpret_cast<const char*>(&buf32), 4);
buf32 = htonl(mSourceIp);
output.write(reinterpret_cast<const char*>(&buf32), 4);
buf16 = htons(mSourcePort);
output.write(reinterpret_cast<const char*>(&buf16), 2);
buf16 = 0; // padding
output.write(reinterpret_cast<const char*>(&buf16), 2);
// --- 3. Packet records ---
size_t written = 0;
for (const auto& pkt : mPacketList) {
if (pkt.mRawData.empty())
continue;
uint16_t plen = static_cast<uint16_t>(pkt.mRawData.size());
uint16_t recLength = static_cast<uint16_t>(plen + 8);
buf16 = htons(recLength);
output.write(reinterpret_cast<const char*>(&buf16), 2);
buf16 = htons(plen);
output.write(reinterpret_cast<const char*>(&buf16), 2);
buf32 = htonl(pkt.mOffsetMs);
output.write(reinterpret_cast<const char*>(&buf32), 4);
output.write(reinterpret_cast<const char*>(pkt.mRawData.data()), plen);
written++;
} }
fclose(f);
}
#endif
if (!output.good())
throw std::runtime_error("Failed to write rtpdump file: " + mFilename);
ICELogInfo(<< "Wrote " << written << " packets to " << mFilename);
}
void RtpDump::clear()
{
mPacketList.clear();
mLoaded = false;
mRecording = false;
}

View File

@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2025 VoIPobjects (voipobjects.com) /* Copyright(C) 2007-2026 VoIPobjects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -6,12 +6,14 @@
#ifndef __HL_RTP_H #ifndef __HL_RTP_H
#define __HL_RTP_H #define __HL_RTP_H
#if defined(USE_RTPDUMP) #include "jrtplib/src/rtppacket.h"
# include "jrtplib/src/rtppacket.h"
#endif
#include <cstdint> #include <cstdint>
#include <stdlib.h> #include <cstdlib>
#include <vector>
#include <string>
#include <memory>
#include <chrono>
// Class to carry rtp/rtcp socket pair // Class to carry rtp/rtcp socket pair
template<class T> template<class T>
@@ -27,7 +29,7 @@ struct RtpPair
:mRtp(rtp), mRtcp(rtcp) :mRtp(rtp), mRtcp(rtcp)
{} {}
bool multiplexed() { return mRtp == mRtcp; } bool multiplexed() const { return mRtp == mRtcp; }
}; };
class RtpHelper class RtpHelper
@@ -35,39 +37,114 @@ class RtpHelper
public: public:
static bool isRtp(const void* buffer, size_t length); static bool isRtp(const void* buffer, size_t length);
static int findPtype(const void* buffer, size_t length); static int findPtype(const void* buffer, size_t length);
static int findPacketNo(const void* buffer, size_t length); static int findPacketNo(const void *buffer, size_t length);
static bool isRtpOrRtcp(const void* buffer, size_t length); static bool isRtpOrRtcp(const void* buffer, size_t length);
static bool isRtcp(const void* buffer, size_t length); static bool isRtcp(const void* buffer, size_t length);
static unsigned findSsrc(const void* buffer, size_t length); static unsigned findSsrc(const void* buffer, size_t length);
static void setSsrc(void* buffer, size_t length, uint32_t ssrc); static void setSsrc(void* buffer, size_t length, uint32_t ssrc);
static int findPayloadLength(const void* buffer, size_t length); static int findPayloadLength(const void* buffer, size_t length);
static std::chrono::microseconds toMicroseconds(const jrtplib::RTPTime& t);
}; };
#if defined(USE_RTPDUMP) /**
* @brief Standard rtpdump file format (rtptools / Wireshark compatible)
*
* Conforms to the rtpdump format defined by rtptools:
* https://formats.kaitai.io/rtpdump/
*
* File layout:
* 1. Text header line:
* "#!rtpplay1.0 <source_ip>/<source_port>\n"
*
* 2. Binary file header (RD_hdr_t, 16 bytes, all big-endian):
* uint32_t start_sec - recording start time, seconds since epoch
* uint32_t start_usec - recording start time, microseconds
* uint32_t source_ip - source IP address (network byte order)
* uint16_t source_port - source port
* uint16_t padding - always 0
*
* 3. Packet records (repeated until EOF):
* Per-packet header (RD_packet_t, 8 bytes, all big-endian):
* uint16_t length - total record length (this 8-byte header + plen)
* uint16_t plen - RTP/RTCP payload length in bytes
* uint32_t offset - milliseconds since recording start
* Followed by plen bytes of RTP/RTCP packet data.
*
* Maximum single packet payload: 65535 bytes (enforced for safety).
*/
class RtpDump class RtpDump
{ {
protected: protected:
struct RtpData struct RtpData
{ {
jrtplib::RTPPacket* mPacket; std::shared_ptr<jrtplib::RTPPacket> mPacket;
void* mData; std::vector<uint8_t> mRawData;
size_t mLength; uint32_t mOffsetMs = 0;
}; };
typedef std::vector<RtpData> PacketList; typedef std::vector<RtpData> PacketList;
PacketList mPacketList; PacketList mPacketList;
std::string mFilename; std::string mFilename;
bool mLoaded = false;
// File header fields
uint32_t mSourceIp = 0;
uint16_t mSourcePort = 0;
uint32_t mStartSec = 0;
uint32_t mStartUsec = 0;
// Auto-compute packet offsets during recording
bool mRecording = false;
std::chrono::steady_clock::time_point mRecordStart;
std::shared_ptr<jrtplib::RTPPacket> parseRtpData(const uint8_t* data, size_t len);
public: public:
RtpDump(const char* filename); explicit RtpDump(const char* filename);
~RtpDump(); ~RtpDump();
/** Set source address for the file header (host byte order). */
void setSource(uint32_t ip, uint16_t port);
uint32_t sourceIp() const { return mSourceIp; }
uint16_t sourcePort() const { return mSourcePort; }
/**
* @brief Load packets from an rtpdump file
* @throws std::runtime_error on file/format error
*/
void load(); void load();
bool isLoaded() const { return mLoaded; }
size_t count() const; size_t count() const;
/**
* @brief Get parsed RTP packet at index
* @throws std::out_of_range if index is invalid
* @throws std::runtime_error if packet could not be parsed as RTP
*/
jrtplib::RTPPacket& packetAt(size_t index); jrtplib::RTPPacket& packetAt(size_t index);
/** @brief Get raw packet bytes at index */
const std::vector<uint8_t>& rawDataAt(size_t index) const;
/** @brief Get packet time offset in milliseconds */
uint32_t offsetAt(size_t index) const;
/** @brief Add a packet; time offset is auto-computed from first add() call */
void add(const void* data, size_t len); void add(const void* data, size_t len);
/** @brief Add a packet with an explicit millisecond offset */
void add(const void* data, size_t len, uint32_t offsetMs);
/**
* @brief Write all packets to file in rtpdump format
* @throws std::runtime_error on file error
*/
void flush(); void flush();
void clear();
const std::string& filename() const { return mFilename; }
}; };
#endif
#endif #endif

View File

@@ -280,7 +280,8 @@ size_t TimerQueue::cancel(uint64_t id) {
//! Cancels all timers //! Cancels all timers
// \return // \return
// The number of timers cancelled // The number of timers cancelled
size_t TimerQueue::cancelAll() { size_t TimerQueue::cancelAll()
{
// Setting all "end" to 0 (for immediate execution) is ok, // Setting all "end" to 0 (for immediate execution) is ok,
// since it maintains the heap integrity // since it maintains the heap integrity
std::unique_lock<std::mutex> lk(m_mtx); std::unique_lock<std::mutex> lk(m_mtx);

View File

@@ -65,6 +65,7 @@ void thread_pool::run_worker()
tasks.pop(); tasks.pop();
} }
} }
t(); // function<void()> type if (t)
t(); // function<void()> type
} }
} }

View File

@@ -240,7 +240,7 @@ int64_t Variant::asInt64() const
if (mType != VTYPE_INT64) if (mType != VTYPE_INT64)
throw Exception(ERR_BAD_VARIANT_TYPE); throw Exception(ERR_BAD_VARIANT_TYPE);
return mInt; return mInt64;
} }
bool Variant::asBool() const bool Variant::asBool() const

View File

@@ -3,11 +3,8 @@
#include "MT_AmrCodec.h" #include "MT_AmrCodec.h"
#include "../helper/HL_ByteBuffer.h" #include "../helper/HL_ByteBuffer.h"
#include "../helper/HL_Log.h"
#include "../helper/HL_IuUP.h" #include "../helper/HL_IuUP.h"
#include "../helper/HL_Exception.h" #include "../helper/HL_Log.h"
#include <iostream>
#define LOG_SUBSYSTEM "AmrCodec" #define LOG_SUBSYSTEM "AmrCodec"
using namespace MT; using namespace MT;
@@ -31,37 +28,37 @@ const uint16_t amrwb_framelenbits[10] =
struct AmrPayloadInfo struct AmrPayloadInfo
{ {
const uint8_t* mPayload; const uint8_t* mPayload = nullptr;
int mPayloadLength; int mPayloadLength = 0;
bool mOctetAligned; bool mOctetAligned = false;
bool mInterleaving; bool mInterleaving = false;
bool mWideband; bool mWideband = false;
uint64_t mCurrentTimestamp; uint64_t mCurrentTimestamp = 0;
}; };
struct AmrFrame struct AmrFrame
{ {
uint8_t mFrameType; uint8_t mFrameType = 0;
uint8_t mMode; uint8_t mMode = 0;
bool mGoodQuality; bool mGoodQuality = false;
uint64_t mTimestamp; uint64_t mTimestamp = 0;
std::shared_ptr<ByteBuffer> mData; std::shared_ptr<ByteBuffer> mData;
uint8_t mSTI; uint8_t mSTI = 0;
}; };
struct AmrPayload struct AmrPayload
{ {
uint8_t mCodeModeRequest; uint8_t mCodeModeRequest = 0;
std::vector<AmrFrame> mFrames; std::vector<AmrFrame> mFrames;
bool mDiscardPacket; bool mDiscardPacket = false;
}; };
// ARM RTP payload has next structure // ARM RTP payload has next structure
// Header // Header
// Table of Contents // Table of Contents
// Frames // Frames
static AmrPayload parseAmrPayload(AmrPayloadInfo& input) static AmrPayload parseAmrPayload(AmrPayloadInfo& input, size_t& cngCounter)
{ {
AmrPayload result; AmrPayload result;
@@ -128,6 +125,8 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
frame.mTimestamp = input.mCurrentTimestamp; frame.mTimestamp = input.mCurrentTimestamp;
result.mFrames.push_back(frame); result.mFrames.push_back(frame);
input.mCurrentTimestamp += input.mWideband ? 320 : 160; input.mCurrentTimestamp += input.mWideband ? 320 : 160;
if (FT == SID_FT)
cngCounter++;
} }
while (F != 0); while (F != 0);
@@ -140,13 +139,17 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
// avoid the loss of data synchronization in the depacketization // avoid the loss of data synchronization in the depacketization
// process, which can result in a huge degradation in speech quality. // process, which can result in a huge degradation in speech quality.
bool discard = input.mWideband ? (f.mFrameType >= 10 && f.mFrameType <= 13) : (f.mFrameType >= 9 && f.mFrameType <= 14); bool discard = input.mWideband ? (f.mFrameType >= 10 && f.mFrameType <= 13) : (f.mFrameType >= 9 && f.mFrameType <= 14);
// discard |= input.mWideband ? f.mFrameType >= 14 : f.mFrameType >= 15;
if (discard) if (discard)
{ {
result.mDiscardPacket = true; result.mDiscardPacket = true;
continue; continue;
} }
// if (input.mWideband && f.mMode == 0xFF /* CNG */)
// {
// int a = 1;
// }`
if (input.mWideband && f.mFrameType == 15) if (input.mWideband && f.mFrameType == 15)
{ {
// DTX, no sense to decode the data // DTX, no sense to decode the data
@@ -165,8 +168,8 @@ static AmrPayload parseAmrPayload(AmrPayloadInfo& input)
continue; continue;
} }
size_t bitsLength = input.mWideband ? amrwb_framelenbits[f.mFrameType] : amrnb_framelenbits[f.mFrameType]; size_t bitsLength = input.mWideband ? amrwb_framelenbits[f.mFrameType] : amrnb_framelenbits[f.mFrameType];
size_t byteLength = input.mWideband ? amrwb_framelen[f.mFrameType] : amrnb_framelen[f.mFrameType]; size_t byteLength = input.mWideband ? amrwb_framelen[f.mFrameType] : amrnb_framelen[f.mFrameType];
if (bitsLength > 0) if (bitsLength > 0)
{ {
@@ -260,8 +263,7 @@ PCodec AmrNbCodec::CodecFactory::create()
AmrNbCodec::AmrNbCodec(const AmrCodecConfig& config) AmrNbCodec::AmrNbCodec(const AmrCodecConfig& config)
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config), mCurrentDecoderTimestamp(0), :mConfig(config)
mSwitchCounter(0), mPreviousPacketLength(0)
{ {
mEncoderCtx = Encoder_Interface_init(1); mEncoderCtx = Encoder_Interface_init(1);
mDecoderCtx = Decoder_Interface_init(); mDecoderCtx = Decoder_Interface_init();
@@ -282,44 +284,31 @@ AmrNbCodec::~AmrNbCodec()
} }
} }
const char* AmrNbCodec::name() Codec::Info AmrNbCodec::info()
{ {
return MT_AMRNB_CODECNAME; return {
.mName = MT_AMRNB_CODECNAME,
.mSamplerate = 8000,
.mChannels = 1,
.mPcmLength = 20 * 16,
.mFrameTime = 20,
.mRtpLength = 0
};
} }
int AmrNbCodec::pcmLength() Codec::EncodeResult AmrNbCodec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
return 20 * 16; if (input.size_bytes() % pcmLength())
} return {.mEncoded = 0};
int AmrNbCodec::rtpLength()
{
return 0;
}
int AmrNbCodec::frameTime()
{
return 20;
}
int AmrNbCodec::samplerate()
{
return 8000;
}
int AmrNbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{
if (inputBytes % pcmLength())
return 0;
// Declare the data input pointer // Declare the data input pointer
auto *dataIn = (const short *)input; auto *dataIn = (const short *)input.data();
// Declare the data output pointer // Declare the data output pointer
auto *dataOut = (unsigned char *)output; auto *dataOut = (unsigned char *)output.data();
// Find how much RTP frames will be generated // Find how much RTP frames will be generated
unsigned int frames = inputBytes / pcmLength(); unsigned int frames = input.size_bytes() / pcmLength();
// Generate frames // Generate frames
for (unsigned int i = 0; i < frames; i++) for (unsigned int i = 0; i < frames; i++)
@@ -328,28 +317,28 @@ int AmrNbCodec::encode(const void* input, int inputBytes, void* output, int outp
dataIn += pcmLength() / 2; dataIn += pcmLength() / 2;
} }
return dataOut - (unsigned char*)output; return {.mEncoded = (size_t)(dataOut - (unsigned char*)output.data())};
} }
#define L_FRAME 160 #define L_FRAME 160
#define AMR_BITRATE_DTX 15 #define AMR_BITRATE_DTX 15
int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) Codec::DecodeResult AmrNbCodec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
if (mConfig.mOctetAligned) if (mConfig.mOctetAligned)
return 0; return {.mDecoded = 0};
if (mConfig.mIuUP) if (mConfig.mIuUP)
{ {
// Try to parse IuUP frame // Try to parse IuUP frame
IuUP::Frame frame; IuUP::Frame frame;
if (!IuUP::parse2((const uint8_t*)input, inputBytes, frame)) if (!IuUP::parse2((const uint8_t*)input.data(), input.size_bytes(), frame))
return 0; return {0};
// Check if CRC failed - it is check from IuUP data // Check if CRC failed - it is check from IuUP data
if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk) if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk)
{ {
ICELogInfo(<< "CRC check failed."); ICELogInfo(<< "CRC check failed.");
return 0; return {0};
} }
// Build NB frame to decode // Build NB frame to decode
@@ -366,61 +355,61 @@ int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outp
// Check if frameType comparing is correct // Check if frameType comparing is correct
if (frameType == 0xFF) if (frameType == 0xFF)
return 0; return {0};
dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2); dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2);
Decoder_Interface_Decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output, 0); Decoder_Interface_Decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output.data(), 0);
return pcmLength(); return {.mDecoded = (size_t)pcmLength()};
} }
else else
{ {
if (outputCapacity < pcmLength()) if (output.size_bytes() < pcmLength())
return 0; return {.mDecoded = 0};
if (inputBytes == 0) if (input.size_bytes() == 0)
{ // PLC part { // PLC part
unsigned char buffer[32]; unsigned char buffer[32];
buffer[0] = (AMR_BITRATE_DTX << 3)|4; buffer[0] = (AMR_BITRATE_DTX << 3)|4;
Decoder_Interface_Decode(mDecoderCtx, buffer, (short*)output, 0); // Handle missing data Decoder_Interface_Decode(mDecoderCtx, buffer, (short*)output.data(), 0); // Handle missing data
return pcmLength(); return {.mDecoded = (size_t)pcmLength()};
} }
AmrPayloadInfo info; AmrPayloadInfo info;
info.mCurrentTimestamp = mCurrentDecoderTimestamp; info.mCurrentTimestamp = mCurrentDecoderTimestamp;
info.mOctetAligned = mConfig.mOctetAligned; info.mOctetAligned = mConfig.mOctetAligned;
info.mPayload = (const uint8_t*)input; info.mPayload = input.data();
info.mPayloadLength = inputBytes; info.mPayloadLength = input.size_bytes();
info.mWideband = false; info.mWideband = false;
info.mInterleaving = false; info.mInterleaving = false;
AmrPayload ap; AmrPayload ap;
try try
{ {
ap = parseAmrPayload(info); ap = parseAmrPayload(info, mCngCounter);
} }
catch(...) catch(...)
{ {
ICELogDebug(<< "Failed to decode AMR payload."); ICELogDebug(<< "Failed to decode AMR payload.");
return 0; return {.mDecoded = 0};
} }
// Save current timestamp // Save current timestamp
mCurrentDecoderTimestamp = info.mCurrentTimestamp; mCurrentDecoderTimestamp = info.mCurrentTimestamp;
// Check if packet is corrupted // Check if packet is corrupted
if (ap.mDiscardPacket) if (ap.mDiscardPacket)
return 0; return {.mDecoded = 0};
// Check for output buffer capacity // Check for output buffer capacity
if (outputCapacity < (int)ap.mFrames.size() * pcmLength()) if (output.size_bytes() < (int)ap.mFrames.size() * pcmLength())
return 0; return {.mDecoded = 0};
if (ap.mFrames.empty()) if (ap.mFrames.empty())
{ {
ICELogError(<< "No AMR frames"); ICELogError(<< "No AMR frames");
} }
short* dataOut = (short*)output; short* dataOut = (short*)output.data();
for (AmrFrame& frame: ap.mFrames) for (AmrFrame& frame: ap.mFrames)
{ {
if (frame.mData) if (frame.mData)
@@ -430,18 +419,18 @@ int AmrNbCodec::decode(const void* input, int inputBytes, void* output, int outp
dataOut += pcmLength() / 2; dataOut += pcmLength() / 2;
} }
} }
return pcmLength() * ap.mFrames.size(); return {.mDecoded = pcmLength() * ap.mFrames.size()};
} }
return pcmLength(); return {.mDecoded = (size_t)pcmLength()};
} }
int AmrNbCodec::plc(int lostFrames, void* output, int outputCapacity) size_t AmrNbCodec::plc(int lostFrames, std::span<uint8_t> output)
{ {
if (outputCapacity < lostFrames * pcmLength()) if (output.size_bytes() < lostFrames * pcmLength())
return 0; return 0;
short* dataOut = (short*)output; short* dataOut = (short*)output.data();
for (int i=0; i < lostFrames; i++) for (int i=0; i < lostFrames; i++)
{ {
@@ -459,6 +448,11 @@ int AmrNbCodec::getSwitchCounter() const
return mSwitchCounter; return mSwitchCounter;
} }
int AmrNbCodec::getCngCounter() const
{
return mCngCounter;
}
// -------- AMR WB codec // -------- AMR WB codec
AmrWbCodec::CodecFactory::CodecFactory(const AmrCodecConfig& config) AmrWbCodec::CodecFactory::CodecFactory(const AmrCodecConfig& config)
:mConfig(config) :mConfig(config)
@@ -500,11 +494,9 @@ PCodec AmrWbCodec::CodecFactory::create()
AmrWbStatistics MT::GAmrWbStatistics; AmrWbStatistics MT::GAmrWbStatistics;
AmrWbCodec::AmrWbCodec(const AmrCodecConfig& config) AmrWbCodec::AmrWbCodec(const AmrCodecConfig& config)
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mConfig(config), :mConfig(config)
mSwitchCounter(0), mPreviousPacketLength(0)
{ {
mDecoderCtx = D_IF_init(); mDecoderCtx = D_IF_init();
mCurrentDecoderTimestamp = 0;
} }
AmrWbCodec::~AmrWbCodec() AmrWbCodec::~AmrWbCodec()
@@ -522,49 +514,37 @@ AmrWbCodec::~AmrWbCodec()
} }
} }
const char* AmrWbCodec::name() Codec::Info AmrWbCodec::info() {
{ return {
return MT_AMRWB_CODECNAME; .mName = MT_AMRWB_CODECNAME,
.mSamplerate = 16000,
.mChannels = 1,
.mPcmLength = 20 * 16 * 2,
.mFrameTime = 20,
.mRtpLength = 0 /* There is complex structure inside AMR packet which may include multilple frames with various length. */
};
} }
int AmrWbCodec::pcmLength()
{
return 20 * 16 * 2;
}
int AmrWbCodec::rtpLength() Codec::EncodeResult AmrWbCodec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
return 0; // VBR // Still no support for encoding - emit silence instead
} return {.mEncoded = 0};
int AmrWbCodec::frameTime()
{
return 20;
}
int AmrWbCodec::samplerate()
{
return 16000;
}
int AmrWbCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{
throw Exception(ERR_NOT_IMPLEMENTED);
} }
#define L_FRAME 160 #define L_FRAME 160
#define AMR_BITRATE_DTX 15 #define AMR_BITRATE_DTX 15
int AmrWbCodec::decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output) Codec::DecodeResult AmrWbCodec::decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
IuUP::Frame frame; IuUP::Frame frame;
if (!IuUP::parse2(input.data(), input.size(), frame)) if (!IuUP::parse2(input.data(), input.size(), frame))
return 0; return {.mDecoded = 0};
if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk) if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk)
{ {
ICELogInfo(<< "CRC check failed."); ICELogInfo(<< "CRC check failed.");
return 0; return {.mDecoded = 0};
} }
// Reserve space // Reserve space
@@ -579,34 +559,34 @@ int AmrWbCodec::decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> ou
frameType = ftIndex; frameType = ftIndex;
if (frameType == 0xFF) if (frameType == 0xFF)
return 0; return {.mDecoded = 0, .mIsCng = true};
dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2); dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2);
D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output.data(), 0); D_IF_decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output.data(), 0);
return pcmLength(); return {.mDecoded = (size_t)pcmLength()};
} }
int AmrWbCodec::decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output) Codec::DecodeResult AmrWbCodec::decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
AmrPayloadInfo info; AmrPayloadInfo info;
info.mCurrentTimestamp = mCurrentDecoderTimestamp; info.mCurrentTimestamp = mCurrentDecoderTimestamp;
info.mOctetAligned = mConfig.mOctetAligned; info.mOctetAligned = mConfig.mOctetAligned;
info.mPayload = input.data(); info.mPayload = input.data();
info.mPayloadLength = input.size(); info.mPayloadLength = input.size();
info.mWideband = true; info.mWideband = true;
info.mInterleaving = false; info.mInterleaving = false;
AmrPayload ap; AmrPayload ap;
try try
{ {
ap = parseAmrPayload(info); ap = parseAmrPayload(info, mCngCounter);
} }
catch(...) catch(...)
{ {
GAmrWbStatistics.mNonParsed++; GAmrWbStatistics.mNonParsed++;
ICELogDebug(<< "Failed to decode AMR payload"); ICELogDebug(<< "Failed to decode AMR payload");
return 0; return {.mDecoded = 0};
} }
// Save current timestamp // Save current timestamp
mCurrentDecoderTimestamp = info.mCurrentTimestamp; mCurrentDecoderTimestamp = info.mCurrentTimestamp;
@@ -615,58 +595,67 @@ int AmrWbCodec::decodePlain(std::span<const uint8_t> input, std::span<uint8_t> o
if (ap.mDiscardPacket) if (ap.mDiscardPacket)
{ {
GAmrWbStatistics.mDiscarded++; GAmrWbStatistics.mDiscarded++;
return 0; return {.mDecoded = 0};
} }
// Check for output buffer capacity // Find the required output capacity
if (output.size() < (int)ap.mFrames.size() * pcmLength()) size_t capacity = 0;
return 0; for (AmrFrame& frame: ap.mFrames)
capacity += frame.mMode == 0xFF /* CNG */ ? pcmLength() : pcmLength();
if (output.size() < capacity)
return {.mDecoded = 0};
short* dataOut = (short*)output.data(); short* dataOut = (short*)output.data();
size_t dataOutSizeInBytes = 0; size_t dataOutSizeInBytes = 0;
for (AmrFrame& frame: ap.mFrames) for (AmrFrame& frame: ap.mFrames)
{ {
memset(dataOut, 0, static_cast<size_t>(output.size())); size_t frameOutputSize = frame.mMode == 0xFF ? pcmLength() : pcmLength();
memset(dataOut, 0, frameOutputSize);
if (frame.mData) if (frame.mData)
{ {
if (frame.mMode == 0xFF)
{
// int bp = 1;
}
D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0); D_IF_decode(mDecoderCtx, (const unsigned char*)frame.mData->data(), (short*)dataOut, 0);
dataOut += pcmLength() / 2; dataOut += frameOutputSize / 2;
dataOutSizeInBytes += pcmLength(); dataOutSizeInBytes += frameOutputSize;
} }
} }
return dataOutSizeInBytes; return {.mDecoded = dataOutSizeInBytes,
.mIsCng = ap.mFrames.size() == 1 ? (ap.mFrames.front().mMode == 0xFF) : false};
} }
int AmrWbCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) Codec::DecodeResult AmrWbCodec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
auto inputBuffer = std::span<const uint8_t>((uint8_t*)input, (size_t)inputBytes);
auto outputBuffer = std::span<uint8_t>((uint8_t*)output, (size_t)outputCapacity);
if (mConfig.mIuUP) if (mConfig.mIuUP)
return decodeIuup(inputBuffer, outputBuffer); return decodeIuup(input, output);
else else
return decodePlain(inputBuffer, outputBuffer); return decodePlain(input, output);
return 0;
} }
int AmrWbCodec::plc(int lostFrames, void* output, int outputCapacity) size_t AmrWbCodec::plc(int lostFrames, std::span<uint8_t> output)
{ {
/* if (outputCapacity < lostFrames * pcmLength()) // ToDo: Check again if PLC works for AMR-WB
return 0; // For now return the silence
memset(output.data(), 0, output.size_bytes());
short* dataOut = (short*)output;
for (int i=0; i < lostFrames; i++)
{
unsigned char buffer[32];
buffer[0] = (AMR_BITRATE_DTX << 3)|4;
Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data
dataOut += L_FRAME;
}
*/
return lostFrames * pcmLength(); return lostFrames * pcmLength();
/*
if (outputCapacity < lostFrames * pcmLength())
return 0;
short* dataOut = (short*)output;
for (int i=0; i < lostFrames; i++)
{
unsigned char buffer[32];
buffer[0] = (AMR_BITRATE_DTX << 3)|4;
Decoder_Interface_Decode(mDecoderCtx, buffer, dataOut, 0); // Handle missing data
dataOut += L_FRAME;
}
*/
} }
int AmrWbCodec::getSwitchCounter() const int AmrWbCodec::getSwitchCounter() const
@@ -674,14 +663,16 @@ int AmrWbCodec::getSwitchCounter() const
return mSwitchCounter; return mSwitchCounter;
} }
int AmrWbCodec::getCngCounter() const
{
return mCngCounter;
}
// ------------- GSM EFR ----------------- // ------------- GSM EFR -----------------
GsmEfrCodec::GsmEfrFactory::GsmEfrFactory(bool iuup, int ptype) GsmEfrCodec::GsmEfrFactory::GsmEfrFactory(bool iuup, int ptype)
:mIuUP(iuup), mPayloadType(ptype) :mIuUP(iuup), mPayloadType(ptype)
{ {}
}
const char* GsmEfrCodec::GsmEfrFactory::name() const char* GsmEfrCodec::GsmEfrFactory::name()
{ {
@@ -699,9 +690,7 @@ int GsmEfrCodec::GsmEfrFactory::payloadType()
} }
void GsmEfrCodec::GsmEfrFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) void GsmEfrCodec::GsmEfrFactory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {}
}
int GsmEfrCodec::GsmEfrFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) int GsmEfrCodec::GsmEfrFactory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {
@@ -719,7 +708,7 @@ PCodec GsmEfrCodec::GsmEfrFactory::create()
} }
GsmEfrCodec::GsmEfrCodec(bool iuup) GsmEfrCodec::GsmEfrCodec(bool iuup)
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mIuUP(iuup) :mIuUP(iuup)
{ {
mEncoderCtx = Encoder_Interface_init(1); mEncoderCtx = Encoder_Interface_init(1);
mDecoderCtx = Decoder_Interface_init(); mDecoderCtx = Decoder_Interface_init();
@@ -740,44 +729,31 @@ GsmEfrCodec::~GsmEfrCodec()
} }
} }
const char* GsmEfrCodec::name() Codec::Info GsmEfrCodec::info()
{ {
return MT_GSMEFR_CODECNAME; return {
.mName = MT_GSMEFR_CODECNAME,
.mSamplerate = 8000,
.mChannels = 1,
.mPcmLength = 20 * 16,
.mFrameTime = 20,
.mRtpLength = 0
};
} }
int GsmEfrCodec::pcmLength() Codec::EncodeResult GsmEfrCodec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
return 20 * 16; if (input.size_bytes() % pcmLength())
} return {.mEncoded = 0};
int GsmEfrCodec::rtpLength()
{
return 0;
}
int GsmEfrCodec::frameTime()
{
return 20;
}
int GsmEfrCodec::samplerate()
{
return 8000;
}
int GsmEfrCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{
if (inputBytes % pcmLength())
return 0;
// Declare the data input pointer // Declare the data input pointer
const short *dataIn = (const short *)input; const short *dataIn = (const short *)input.data();
// Declare the data output pointer // Declare the data output pointer
unsigned char *dataOut = (unsigned char *)output; unsigned char *dataOut = (unsigned char *)output.data();
// Find how much RTP frames will be generated // Find how much RTP frames will be generated
unsigned int frames = inputBytes / pcmLength(); unsigned int frames = input.size_bytes() / pcmLength();
// Generate frames // Generate frames
for (unsigned int i = 0; i < frames; i++) for (unsigned int i = 0; i < frames; i++)
@@ -786,7 +762,7 @@ int GsmEfrCodec::encode(const void* input, int inputBytes, void* output, int out
dataIn += pcmLength() / 2; dataIn += pcmLength() / 2;
} }
return frames * rtpLength(); return {.mEncoded = frames * rtpLength()};
} }
#define L_FRAME 160 #define L_FRAME 160
@@ -843,100 +819,61 @@ const uint16_t gsm690_12_2_bitorder[244] = {
237, 236, 96, 199, 237, 236, 96, 199,
}; };
int GsmEfrCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) Codec::DecodeResult GsmEfrCodec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
/* if (mIuUP) if (output.size_bytes() < pcmLength())
{ return {.mDecoded = 0};
// Try to parse IuUP frame
IuUP::Frame frame;
if (!IuUP::parse2((const uint8_t*)input, inputBytes, frame))
return 0;
// Check if CRC failed - it is check from IuUP data if (input.size_bytes() == 0)
if (!frame.mHeaderCrcOk || !frame.mPayloadCrcOk) { // PLC part
{ unsigned char buffer[32];
ICELogInfo(<< "CRC check failed."); buffer[0] = (AMR_BITRATE_DTX << 3)|4;
return 0; Decoder_Interface_Decode(mDecoderCtx, buffer, (short*)output.data(), 0); // Handle missing data
} }
else
// Build NB frame to decode
ByteBuffer dataToDecode;
dataToDecode.resize(1 + frame.mPayloadSize); // Reserve place
// Copy AMR data
memmove(dataToDecode.mutableData() + 1, frame.mPayload, frame.mPayloadSize);
uint8_t frameType = 0xFF;
for (uint8_t ftIndex = 0; ftIndex <= 9 && frameType == 0xFF; ftIndex++)
if (amrnb_framelen[ftIndex] == frame.mPayloadSize)
frameType = ftIndex;
// Check if frameType comparing is correct
if (frameType == 0xFF)
return 0;
dataToDecode.mutableData()[0] = (frameType << 3) | (1 << 2);
Decoder_Interface_Decode(mDecoderCtx, (const unsigned char*)dataToDecode.data(), (short*)output, 0);
return pcmLength();
}
else */
{ {
if (outputCapacity < pcmLength()) // Reorder bytes from input to dst
return 0; uint8_t dst[GSM_EFR_FRAME_LEN];
const uint8_t* src = input.data();
for (int i=0; i<(GSM_EFR_FRAME_LEN-1); i++)
dst[i] = (src[i] << 4) | (src[i+1] >> 4);
dst[GSM_EFR_FRAME_LEN-1] = src[GSM_EFR_FRAME_LEN-1] << 4;
if (inputBytes == 0) unsigned char in[GSM_EFR_FRAME_LEN + 1];
{ // PLC part
unsigned char buffer[32]; // Reorder bits
buffer[0] = (AMR_BITRATE_DTX << 3)|4; in[0] = 0x3c; /* AMR mode 7 = GSM-EFR, Quality bit is set */
Decoder_Interface_Decode(mDecoderCtx, buffer, (short*)output, 0); // Handle missing data in[GSM_EFR_FRAME_LEN] = 0x0;
}
else for (int i=0; i<244; i++)
{ {
// Reorder bytes from input to dst int si = gsm690_12_2_bitorder[i];
uint8_t dst[GSM_EFR_FRAME_LEN]; int di = i;
const uint8_t* src = (const uint8_t*)input; msb_put_bit(in + 1, di, msb_get_bit(dst, si));
for (int i=0; i<(GSM_EFR_FRAME_LEN-1); i++) }
dst[i] = (src[i] << 4) | (src[i+1] >> 4);
dst[GSM_EFR_FRAME_LEN-1] = src[GSM_EFR_FRAME_LEN-1] << 4;
unsigned char in[GSM_EFR_FRAME_LEN + 1]; // Decode
memset(output.data(), 0, pcmLength());
Decoder_Interface_Decode(mDecoderCtx, in, (short*)output.data(), 0);
// Reorder bits uint8_t* pcm = (uint8_t*)output.data();
in[0] = 0x3c; /* AMR mode 7 = GSM-EFR, Quality bit is set */ for (int i=0; i<160; i++)
in[GSM_EFR_FRAME_LEN] = 0x0; {
uint16_t w = ((uint16_t*)output.data())[i];
for (int i=0; i<244; i++) pcm[(i<<1) ] = w & 0xff;
{ pcm[(i<<1)+1] = (w >> 8) & 0xff;
int si = gsm690_12_2_bitorder[i];
int di = i;
msb_put_bit(in + 1, di, msb_get_bit(dst, si));
}
// Decode
memset(output, 0, pcmLength());
Decoder_Interface_Decode(mDecoderCtx, in, (short*)output, 0);
uint8_t* pcm = (uint8_t*)output;
for (int i=0; i<160; i++)
{
uint16_t w = ((uint16_t*)output)[i];
pcm[(i<<1) ] = w & 0xff;
pcm[(i<<1)+1] = (w >> 8) & 0xff;
}
} }
} }
return pcmLength(); return {.mDecoded = (size_t)pcmLength()};
} }
int GsmEfrCodec::plc(int lostFrames, void* output, int outputCapacity) size_t GsmEfrCodec::plc(int lostFrames, std::span<uint8_t> output)
{ {
if (outputCapacity < lostFrames * pcmLength()) if (output.size_bytes() < lostFrames * pcmLength())
return 0; return 0;
short* dataOut = (short*)output; short* dataOut = (short*)output.data();
for (int i=0; i < lostFrames; i++) for (int i=0; i < lostFrames; i++)
{ {

View File

@@ -26,13 +26,13 @@ struct AmrCodecConfig
class AmrNbCodec : public Codec class AmrNbCodec : public Codec
{ {
protected: protected:
void* mEncoderCtx; void* mEncoderCtx = nullptr;
void* mDecoderCtx; void* mDecoderCtx = nullptr;
AmrCodecConfig mConfig; AmrCodecConfig mConfig;
unsigned mCurrentDecoderTimestamp; unsigned mCurrentDecoderTimestamp = 0;
int mSwitchCounter; int mPreviousPacketLength = 0;
int mPreviousPacketLength; size_t mCngCounter = 0;
size_t mSwitchCounter = 0;
public: public:
class CodecFactory: public Factory class CodecFactory: public Factory
{ {
@@ -54,17 +54,16 @@ public:
}; };
AmrNbCodec(const AmrCodecConfig& config); AmrNbCodec(const AmrCodecConfig& config);
~AmrNbCodec();
Info info() override;
EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
size_t plc(int lostFrames, std::span<uint8_t> output) override;
virtual ~AmrNbCodec();
const char* name() override;
int pcmLength() override;
int rtpLength() override;
int frameTime() override;
int samplerate() override;
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int plc(int lostFrames, void* output, int outputCapacity) override;
int getSwitchCounter() const; int getSwitchCounter() const;
int getCngCounter() const;
}; };
struct AmrWbStatistics struct AmrWbStatistics
@@ -77,15 +76,17 @@ extern AmrWbStatistics GAmrWbStatistics;
class AmrWbCodec : public Codec class AmrWbCodec : public Codec
{ {
protected: protected:
void* mEncoderCtx; void* mEncoderCtx = nullptr;
void* mDecoderCtx; void* mDecoderCtx = nullptr;
AmrCodecConfig mConfig; AmrCodecConfig mConfig;
uint64_t mCurrentDecoderTimestamp; uint64_t mCurrentDecoderTimestamp = 0;
int mSwitchCounter; size_t mSwitchCounter = 0;
size_t mCngCounter = 0;
int mPreviousPacketLength; int mPreviousPacketLength;
int decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output); DecodeResult decodeIuup(std::span<const uint8_t> input, std::span<uint8_t> output);
int decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output); DecodeResult decodePlain(std::span<const uint8_t> input, std::span<uint8_t> output);
public: public:
class CodecFactory: public Factory class CodecFactory: public Factory
@@ -110,23 +111,21 @@ public:
AmrWbCodec(const AmrCodecConfig& config); AmrWbCodec(const AmrCodecConfig& config);
virtual ~AmrWbCodec(); virtual ~AmrWbCodec();
const char* name() override; Info info() override;
int pcmLength() override;
int rtpLength() override; EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int frameTime() override; DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int samplerate() override; size_t plc(int lostFrames, std::span<uint8_t> output) override;
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int plc(int lostFrames, void* output, int outputCapacity) override;
int getSwitchCounter() const; int getSwitchCounter() const;
int getCngCounter() const;
}; };
class GsmEfrCodec : public Codec class GsmEfrCodec : public Codec
{ {
protected: protected:
void* mEncoderCtx; void* mEncoderCtx = nullptr;
void* mDecoderCtx; void* mDecoderCtx = nullptr;
bool mIuUP; bool mIuUP = false;
public: public:
class GsmEfrFactory: public Factory class GsmEfrFactory: public Factory
@@ -143,23 +142,19 @@ public:
void create(CodecMap& codecs) override; void create(CodecMap& codecs) override;
PCodec create() override; PCodec create() override;
protected: protected:
bool mIuUP; bool mIuUP;
int mPayloadType; int mPayloadType;
}; };
GsmEfrCodec(bool iuup = false); GsmEfrCodec(bool iuup = false);
~GsmEfrCodec();
virtual ~GsmEfrCodec(); Info info() override;
const char* name() override;
int pcmLength() override; EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int rtpLength() override; DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int frameTime() override; size_t plc(int lostFrames, std::span<uint8_t> output) override;
int samplerate() override;
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int plc(int lostFrames, void* output, int outputCapacity) override;
}; };
} // End of MT namespace } // End of MT namespace

View File

@@ -91,38 +91,20 @@ G729Codec::~G729Codec()
} }
} }
const char* G729Codec::name() Codec::Info G729Codec::info()
{ {
return "G729"; return {
} .mName = "G729",
.mSamplerate = 8000,
int G729Codec::pcmLength() .mChannels = 1,
{ .mPcmLength = 10 * 8 * 2,
return 10 * 8 * 2; .mFrameTime = 10,
} .mRtpLength = 10
};
int G729Codec::rtpLength()
{
return 10;
}
int G729Codec::frameTime()
{
return 10;
}
int G729Codec::samplerate()
{
return 8000;
}
int G729Codec::channels()
{
return 1;
} }
// static const int SamplesPerFrame = 80; // static const int SamplesPerFrame = 80;
int G729Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity) Codec::EncodeResult G729Codec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
// Create encoder if it is not done yet // Create encoder if it is not done yet
if (!mEncoder) if (!mEncoder)
@@ -131,24 +113,24 @@ int G729Codec::encode(const void* input, int inputBytes, void* output, int outpu
if (mEncoder) if (mEncoder)
Init_Pre_Process(mEncoder); Init_Pre_Process(mEncoder);
} }
int result = 0; size_t result = 0;
if (mEncoder) if (mEncoder)
{ {
int nrOfFrames = inputBytes / 160; // 10ms frames int nrOfFrames = input.size_bytes() / 160; // 10ms frames
Word16 parm[PRM_SIZE]; // ITU's service buffer Word16 parm[PRM_SIZE]; // ITU's service buffer
for (int frameIndex = 0; frameIndex < nrOfFrames; frameIndex++) for (int frameIndex = 0; frameIndex < nrOfFrames; frameIndex++)
{ {
Copy((int16_t*)input + frameIndex * pcmLength() / 2, mEncoder->new_speech, pcmLength() / 2); Copy((int16_t*)input.data() + frameIndex * info().mPcmLength / 2, mEncoder->new_speech, info().mPcmLength / 2);
Pre_Process(mEncoder, mEncoder->new_speech, pcmLength() / 2); Pre_Process(mEncoder, mEncoder->new_speech, info().mPcmLength / 2);
Coder_ld8a(mEncoder, parm); Coder_ld8a(mEncoder, parm);
Store_Params(parm, (uint8_t*)output + frameIndex * rtpLength()); Store_Params(parm, output.data() + frameIndex * info().mRtpLength);
result += rtpLength(); result += info().mRtpLength;
} }
} }
return result; return {result};
} }
int G729Codec::decode(const void* input, int inputBytes, void* output, int outputCapacity) Codec::DecodeResult G729Codec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
if (!mDecoder) if (!mDecoder)
{ {
@@ -160,29 +142,29 @@ int G729Codec::decode(const void* input, int inputBytes, void* output, int outpu
} }
} }
int result = 0; size_t result = 0;
if (mDecoder) if (mDecoder)
{ {
// See if there are silence bytes in the end // See if there are silence bytes in the end
bool isSilence = (inputBytes % rtpLength()) / 2 != 0; bool isSilence = (input.size_bytes() % info().mRtpLength) / 2 != 0;
// Find number of frames // Find number of frames
int nrOfFrames = inputBytes / rtpLength(); int nrOfFrames = input.size_bytes() / info().mRtpLength;
nrOfFrames = std::min(outputCapacity / pcmLength(), nrOfFrames); nrOfFrames = std::min(output.size_bytes() / info().mPcmLength, (size_t)nrOfFrames);
for (int frameIndex = 0; frameIndex < nrOfFrames; frameIndex++) for (int frameIndex = 0; frameIndex < nrOfFrames; frameIndex++)
decodeFrame((const uint8_t*)input + frameIndex * rtpLength(), (int16_t*)output + frameIndex * pcmLength()); decodeFrame(input.data() + frameIndex * info().mRtpLength, (int16_t*)output.data() + frameIndex * info().mPcmLength);
result += nrOfFrames * pcmLength(); result += nrOfFrames * info().mPcmLength;
if (isSilence && nrOfFrames < outputCapacity / pcmLength()) if (isSilence && nrOfFrames < output.size_bytes() / info().mPcmLength)
{ {
memset((uint8_t*)output + nrOfFrames * pcmLength(), 0, pcmLength()); memset(output.data() + nrOfFrames * info().mPcmLength, 0, info().mPcmLength);
result += pcmLength(); result += info().mPcmLength;
} }
} }
return result; return {.mDecoded = result, .mIsCng = false};
} }
void G729Codec::decodeFrame(const uint8_t* rtp, int16_t* pcm) void G729Codec::decodeFrame(const uint8_t* rtp, int16_t* pcm)
@@ -217,7 +199,7 @@ void G729Codec::decodeFrame(const uint8_t* rtp, int16_t* pcm)
} }
int G729Codec::plc(int lostFrames, void* output, int outputCapacity) size_t G729Codec::plc(int lostFrames, std::span<uint8_t> output)
{ {
return 0; return 0;
} }
@@ -390,7 +372,7 @@ int OpusCodec::OpusFactory::processSdp(const resip::SdpContents::Session::Medium
PCodec OpusCodec::OpusFactory::create() PCodec OpusCodec::OpusFactory::create()
{ {
OpusCodec* result = new OpusCodec(mSamplerate, mChannels, mParams.mPtime); OpusCodec* result = new OpusCodec(Audio::Format(mSamplerate, mChannels), mParams.mPtime);
result->applyParams(mParams); result->applyParams(mParams);
PCodec c(result); PCodec c(result);
mCodecList.push_back(c); mCodecList.push_back(c);
@@ -398,8 +380,8 @@ PCodec OpusCodec::OpusFactory::create()
return c; return c;
} }
OpusCodec::OpusCodec(int samplerate, int channels, int ptime) OpusCodec::OpusCodec(Audio::Format fmt, int ptime)
:mEncoderCtx(nullptr), mDecoderCtx(nullptr), mChannels(channels), mPTime(ptime), mSamplerate(samplerate), mDecoderChannels(0) :mEncoderCtx(nullptr), mDecoderCtx(nullptr), mChannels(fmt.channels()), mPTime(ptime), mSamplerate(fmt.rate()), mDecoderChannels(0)
{ {
int status; int status;
mEncoderCtx = opus_encoder_create(mSamplerate, mChannels, OPUS_APPLICATION_VOIP, &status); mEncoderCtx = opus_encoder_create(mSamplerate, mChannels, OPUS_APPLICATION_VOIP, &status);
@@ -441,52 +423,34 @@ OpusCodec::~OpusCodec()
} }
} }
const char* OpusCodec::name() Codec::Info OpusCodec::info() {
{ return {
return OPUS_CODEC_NAME; .mName = OPUS_CODEC_NAME,
.mSamplerate = mSamplerate,
.mChannels = mChannels,
.mPcmLength = (int)(mSamplerate / 1000 * sizeof(short) * mChannels * mPTime),
.mFrameTime = mPTime,
.mRtpLength = 0 /* VBR */
};
} }
int OpusCodec::pcmLength() Codec::EncodeResult OpusCodec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{
return (samplerate() / 1000 ) * frameTime() * sizeof(short) * channels();
}
int OpusCodec::channels()
{
return mChannels;
}
int OpusCodec::rtpLength()
{
return 0; // VBR
}
int OpusCodec::frameTime()
{
return mPTime;
}
int OpusCodec::samplerate()
{
return mSamplerate;
}
int OpusCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{ {
// Send number of samples for input and number of bytes for output // Send number of samples for input and number of bytes for output
int written = opus_encode(mEncoderCtx, (const opus_int16*)input, inputBytes / (sizeof(short) * channels()), (unsigned char*)output, outputCapacity / (sizeof(short) * channels())); int written = opus_encode(mEncoderCtx, (const opus_int16*)input.data(), input.size_bytes() / (sizeof(short) * channels()),
output.data(), output.size_bytes() / (sizeof(short) * channels()));
if (written < 0) if (written < 0)
return 0; return {.mEncoded = 0};
else else
return written; return {.mEncoded = (size_t)written};
} }
int OpusCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) Codec::DecodeResult OpusCodec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
int result = 0; int result = 0;
// Examine the number of channels available in incoming packet // Examine the number of channels available in incoming packet
int nr_of_channels = opus_packet_get_nb_channels((const unsigned char *) input); int nr_of_channels = opus_packet_get_nb_channels(input.data());
// Recreate decoder if needed // Recreate decoder if needed
if (mDecoderChannels != nr_of_channels) if (mDecoderChannels != nr_of_channels)
@@ -504,24 +468,22 @@ int OpusCodec::decode(const void* input, int inputBytes, void* output, int outpu
int status = 0; int status = 0;
mDecoderCtx = opus_decoder_create(mSamplerate, mDecoderChannels, &status); mDecoderCtx = opus_decoder_create(mSamplerate, mDecoderChannels, &status);
if (status) if (status)
return 0; return {0};
} }
int nr_of_frames = opus_decoder_get_nb_samples(mDecoderCtx, (const unsigned char *) input, int nr_of_frames = opus_decoder_get_nb_samples(mDecoderCtx, input.data(), input.size_bytes());
inputBytes);
if (nr_of_frames <= 0) if (nr_of_frames <= 0)
return 0; return {0};
// We support stereo and mono here. // We support stereo and mono here.
int buffer_capacity = nr_of_frames * sizeof(opus_int16) * nr_of_channels; int buffer_capacity = nr_of_frames * sizeof(opus_int16) * nr_of_channels;
opus_int16 *buffer_decode = (opus_int16 *)alloca(buffer_capacity); opus_int16 *buffer_decode = (opus_int16 *)alloca(buffer_capacity);
int decoded = opus_decode(mDecoderCtx, int decoded = opus_decode(mDecoderCtx, input.data(), input.size_bytes(),
reinterpret_cast<const unsigned char *>(input), inputBytes,
buffer_decode, nr_of_frames, 0); buffer_decode, nr_of_frames, 0);
if (decoded < 0) if (decoded < 0)
{ {
ICELogCritical(<< "opus_decode() returned " << decoded); ICELogCritical(<< "opus_decode() returned " << decoded);
return 0; return {0};
} }
opus_int16 *buffer_stereo = nullptr; opus_int16 *buffer_stereo = nullptr;
@@ -535,14 +497,14 @@ int OpusCodec::decode(const void* input, int inputBytes, void* output, int outpu
buffer_stereo[i * 2 + 1] = buffer_decode[i]; buffer_stereo[i * 2 + 1] = buffer_decode[i];
buffer_stereo[i * 2] = buffer_decode[i]; buffer_stereo[i * 2] = buffer_decode[i];
} }
assert(buffer_stereo_capacity <= outputCapacity); assert(buffer_stereo_capacity <= output.size_bytes());
memcpy(output, buffer_stereo, buffer_stereo_capacity); memcpy(output.data(), buffer_stereo, buffer_stereo_capacity);
result = buffer_stereo_capacity; result = buffer_stereo_capacity;
break; break;
case 2: case 2:
assert(buffer_capacity <= outputCapacity); assert(buffer_capacity <= output.size_bytes());
memcpy(output, buffer_decode, buffer_capacity); memcpy(output.data(), buffer_decode, buffer_capacity);
result = buffer_capacity; result = buffer_capacity;
break; break;
@@ -550,17 +512,17 @@ int OpusCodec::decode(const void* input, int inputBytes, void* output, int outpu
assert(0); assert(0);
} }
return result; return {.mDecoded = (size_t)result};
} }
int OpusCodec::plc(int lostPackets, void* output, int outputCapacity) size_t OpusCodec::plc(int lostPackets, std::span<uint8_t> output)
{ {
// Find how much frames do we need to produce and prefill it with silence // Find how much frames do we need to produce and prefill it with silence
int frames_per_packet = (int)pcmLength() / (sizeof(opus_int16) * channels()); int frames_per_packet = (int)pcmLength() / (sizeof(opus_int16) * channels());
memset(output, 0, outputCapacity); memset(output.data(), 0, output.size_bytes());
// Use this pointer as output // Use this pointer as output
opus_int16* data_output = reinterpret_cast<opus_int16*>(output); opus_int16* data_output = reinterpret_cast<opus_int16*>(output.data());
int nr_of_decoded_frames = 0; int nr_of_decoded_frames = 0;
@@ -575,10 +537,7 @@ int OpusCodec::plc(int lostPackets, void* output, int outputCapacity)
case 1: case 1:
// Convert mono to stereo // Convert mono to stereo
for (int i=0; i < nr_of_decoded_frames; i++) for (int i=0; i < nr_of_decoded_frames; i++)
{ data_output[i * 2] = data_output[i * 2 + 1] = buffer_plc[i];
data_output[i * 2] = buffer_plc[i];
data_output[i * 2 + 1] = buffer_plc[i+1];
}
data_output += frames_per_packet * mChannels; data_output += frames_per_packet * mChannels;
break; break;
@@ -589,14 +548,14 @@ int OpusCodec::plc(int lostPackets, void* output, int outputCapacity)
break; break;
} }
} }
return ((char*)data_output - (char*)output) * sizeof(opus_int16); return ((uint8_t*)data_output - output.data());
} }
// -------------- ILBC ------------------- // -------------- ILBC -------------------
#define ILBC_CODEC_NAME "ILBC" #define ILBC_CODEC_NAME "ILBC"
IlbcCodec::IlbcCodec(int packetTime) IlbcCodec::IlbcCodec(int packetTime)
:mPacketTime(packetTime), mEncoderCtx(nullptr), mDecoderCtx(nullptr) :mPacketTime(packetTime)
{ {
WebRtcIlbcfix_EncoderCreate(&mEncoderCtx); WebRtcIlbcfix_EncoderCreate(&mEncoderCtx);
WebRtcIlbcfix_DecoderCreate(&mDecoderCtx); WebRtcIlbcfix_DecoderCreate(&mDecoderCtx);
@@ -610,44 +569,31 @@ IlbcCodec::~IlbcCodec()
WebRtcIlbcfix_EncoderFree(mEncoderCtx); WebRtcIlbcfix_EncoderFree(mEncoderCtx);
} }
const char* IlbcCodec::name() Codec::Info IlbcCodec::info()
{ {
return "ilbc"; return {
.mName = ILBC_CODEC_NAME,
.mSamplerate = 8000,
.mChannels = 1,
.mPcmLength = mPacketTime * 8 * (int)sizeof(short),
.mFrameTime = mPacketTime,
.mRtpLength = (mPacketTime == 20) ? 38 : 50
};
} }
int IlbcCodec::rtpLength() Codec::EncodeResult IlbcCodec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
return (mPacketTime == 20 ) ? 38 : 50; if (input.size_bytes() % pcmLength())
} return {};
int IlbcCodec::pcmLength()
{
return mPacketTime * 16;
}
int IlbcCodec::frameTime()
{
return mPacketTime;
}
int IlbcCodec::samplerate()
{
return 8000;
}
int IlbcCodec::encode(const void *input, int inputBytes, void* outputBuffer, int outputCapacity)
{
if (inputBytes % pcmLength())
return 0;
// Declare the data input pointer // Declare the data input pointer
short *dataIn = (short *)input; short *dataIn = (short *)input.data();
// Declare the data output pointer // Declare the data output pointer
char *dataOut = (char *)outputBuffer; char *dataOut = (char *)output.data();
// Find how much RTP frames will be generated // Find how much RTP frames will be generated
unsigned int frames = inputBytes / pcmLength(); unsigned int frames = input.size_bytes() / pcmLength();
// Generate frames // Generate frames
for (unsigned int i=0; i<frames; i++) for (unsigned int i=0; i<frames; i++)
@@ -657,15 +603,15 @@ int IlbcCodec::encode(const void *input, int inputBytes, void* outputBuffer, int
dataOut += rtpLength(); dataOut += rtpLength();
} }
return frames * rtpLength(); return {frames * rtpLength()};
} }
int IlbcCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) Codec::DecodeResult IlbcCodec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
unsigned frames = inputBytes / rtpLength(); unsigned frames = input.size_bytes() / rtpLength();
char* dataIn = (char*)input; char* dataIn = (char*)input.data();
short* dataOut = (short*)output; short* dataOut = (short*)output.data();
for (unsigned i=0; i < frames; ++i) for (unsigned i=0; i < frames; ++i)
{ {
@@ -675,12 +621,12 @@ int IlbcCodec::decode(const void* input, int inputBytes, void* output, int outpu
dataOut += pcmLength() / 2; dataOut += pcmLength() / 2;
} }
return frames * pcmLength(); return {frames * pcmLength()};
} }
int IlbcCodec::plc(int lostFrames, void* output, int outputCapacity) size_t IlbcCodec::plc(int lostFrames, std::span<uint8_t> output)
{ {
return 2 * WebRtcIlbcfix_DecodePlc(mDecoderCtx, (WebRtc_Word16*)output, lostFrames); return sizeof(short) * WebRtcIlbcfix_DecodePlc(mDecoderCtx, (WebRtc_Word16*)output.data(), lostFrames);
} }
// --- IlbcFactory --- // --- IlbcFactory ---
@@ -795,38 +741,24 @@ IsacCodec::~IsacCodec()
WebRtcIsacfix_Free(mDecoderCtx); mDecoderCtx = NULL; WebRtcIsacfix_Free(mDecoderCtx); mDecoderCtx = NULL;
} }
const char* IsacCodec::name() Codec::Info IsacCodec::info() {
{ return {
return "isac"; .mName = "isac",
.mSamplerate = mSamplerate,
.mChannels = 1,
.mPcmLength = 60 * mSamplerate / 1000 * 2,
.mFrameTime = 60,
.mRtpLength = 0
};
} }
int IsacCodec::frameTime() Codec::EncodeResult IsacCodec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
return 60; unsigned nrOfSamples = input.size_bytes() / 2;
}
int IsacCodec::samplerate()
{
return mSamplerate;
}
int IsacCodec::pcmLength()
{
return frameTime() * samplerate() / 1000 * sizeof(short);
}
int IsacCodec::rtpLength()
{
return 0;
}
int IsacCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{
unsigned nrOfSamples = inputBytes / 2;
unsigned timeLength = nrOfSamples / (mSamplerate / 1000); unsigned timeLength = nrOfSamples / (mSamplerate / 1000);
int encoded = 0; int encoded = 0;
char* dataOut = (char*)output; char* dataOut = (char*)output.data();
const WebRtc_Word16* dataIn = (const WebRtc_Word16*)input; const WebRtc_Word16* dataIn = (const WebRtc_Word16*)input.data();
// Iterate 10 milliseconds chunks // Iterate 10 milliseconds chunks
for (unsigned i=0; i<timeLength/10; i++) for (unsigned i=0; i<timeLength/10; i++)
@@ -835,25 +767,25 @@ int IsacCodec::encode(const void* input, int inputBytes, void* output, int outpu
if (encoded > 0) if (encoded > 0)
dataOut += encoded; dataOut += encoded;
} }
return dataOut - (char*)output; return {.mEncoded = (size_t)(dataOut - (char*)output.data())};
} }
int IsacCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) Codec::DecodeResult IsacCodec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
WebRtc_Word16 speechType = 0; WebRtc_Word16 speechType = 0;
unsigned produced = WebRtcIsacfix_Decode(mDecoderCtx, (const WebRtc_UWord16*)input, inputBytes, (WebRtc_Word16*)output, &speechType); unsigned produced = WebRtcIsacfix_Decode(mDecoderCtx, (const WebRtc_UWord16*)input.data(), input.size_bytes(), (WebRtc_Word16*)output.data(), &speechType);
if (produced == (unsigned)-1) if (produced == (unsigned)-1)
return 0; return {.mDecoded = 0};
return produced * 2; return {.mDecoded = produced * 2};
} }
int IsacCodec::plc(int lostFrames, void* output, int outputCapacity) size_t IsacCodec::plc(int lostFrames, std::span<uint8_t> output)
{ {
// lostFrames are 30-milliseconds frames; but used encoding mode is 60 milliseconds. // lostFrames are 30-milliseconds frames; but used encoding mode is 60 milliseconds.
// So lostFrames * 2 // So lostFrames * 2
lostFrames *=2 ; lostFrames *=2 ;
if (-1 == WebRtcIsacfix_DecodePlc(mDecoderCtx, (WebRtc_Word16*)output, lostFrames )) if (-1 == WebRtcIsacfix_DecodePlc(mDecoderCtx, (WebRtc_Word16*)output.data(), lostFrames ))
return 0; return 0;
return lostFrames * 30 * (samplerate()/1000 * sizeof(short)); return lostFrames * 30 * (samplerate()/1000 * sizeof(short));
@@ -916,71 +848,55 @@ PCodec IsacCodec::IsacFactory32K::create()
G711Codec::G711Codec(int type) G711Codec::G711Codec(int type)
:mType(type) :mType(type)
{ {}
}
G711Codec::~G711Codec() G711Codec::~G711Codec()
{ {}
Codec::Info G711Codec::info() {
return {
.mName = mType == ALaw ? "PCMA" : "PCMU",
.mSamplerate = 8000,
.mChannels = 1,
.mPcmLength = 10 * 16,
.mFrameTime = 10,
.mRtpLength = 10 * 8
};
} }
const char* G711Codec::name() Codec::EncodeResult G711Codec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{
return "g711";
}
int G711Codec::pcmLength()
{
return frameTime() * 16;
}
int G711Codec::rtpLength()
{
return frameTime() * 8;
}
int G711Codec::frameTime()
{
return 10;
}
int G711Codec::samplerate()
{
return 8000;
}
int G711Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{ {
int result; int result;
if (mType == ALaw) if (mType == ALaw)
result = WebRtcG711_EncodeA(NULL, (WebRtc_Word16*)input, inputBytes/2, (WebRtc_Word16*)output); result = WebRtcG711_EncodeA(nullptr, (WebRtc_Word16*)input.data(), input.size_bytes() / 2, (WebRtc_Word16*)output.data());
else else
result = WebRtcG711_EncodeU(NULL, (WebRtc_Word16*)input, inputBytes/2, (WebRtc_Word16*)output); result = WebRtcG711_EncodeU(nullptr, (WebRtc_Word16*)input.data(), input.size_bytes() / 2, (WebRtc_Word16*)output.data());
if (result == -1) if (result < 0)
throw Exception(ERR_WEBRTC, -1); return {.mEncoded = 0};
return result; return {.mEncoded = (size_t) result};
} }
int G711Codec::decode(const void* input, int inputBytes, void* output, int outputCapacity) Codec::DecodeResult G711Codec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
assert(outputCapacity >= inputBytes * 2); assert(output.size_bytes() >= input.size_bytes() * 2);
int result; int result;
WebRtc_Word16 speechType; WebRtc_Word16 speechType;
if (mType == ALaw) if (mType == ALaw)
result = WebRtcG711_DecodeA(NULL, (WebRtc_Word16*)input, inputBytes, (WebRtc_Word16*)output, &speechType); result = WebRtcG711_DecodeA(nullptr, (WebRtc_Word16*)input.data(), input.size_bytes(), (WebRtc_Word16*)output.data(), &speechType);
else else
result = WebRtcG711_DecodeU(NULL, (WebRtc_Word16*)input, inputBytes, (WebRtc_Word16*)output, &speechType); result = WebRtcG711_DecodeU(nullptr, (WebRtc_Word16*)input.data(), input.size_bytes(), (WebRtc_Word16*)output.data(), &speechType);
if (result == -1) if (result < 0)
throw Exception(ERR_WEBRTC, -1); return {.mDecoded = 0};
return result * 2; return {.mDecoded = (size_t)result * 2};
} }
int G711Codec::plc(int lostSamples, void* output, int outputCapacity) size_t G711Codec::plc(int lostSamples, std::span<uint8_t> output)
{ {
return 0; return 0;
} }
@@ -1060,86 +976,64 @@ GsmCodec::GsmCodec(Type codecType)
GsmCodec::~GsmCodec() GsmCodec::~GsmCodec()
{ {
gsm_destroy(mGSM); gsm_destroy(mGSM); mGSM = nullptr;
} }
const char* GsmCodec::name() Codec::Info GsmCodec::info() {
{ int rtpLength = 0;
return "GSM-06.10";
}
int GsmCodec::rtpLength()
{
switch (mCodecType) switch (mCodecType)
{ {
case Type::Bytes_31: case Type::Bytes_31: rtpLength = GSM_RTPFRAME_SIZE_31; break;
return GSM_RTPFRAME_SIZE_31; case Type::Bytes_32: rtpLength = GSM_RTPFRAME_SIZE_32; break;
break; case Type::Bytes_33: rtpLength = GSM_RTPFRAME_SIZE_33; break;
case Type::Bytes_65: rtpLength = GSM_RTPFRAME_SIZE_32 + GSM_RTPFRAME_SIZE_33; break;
case Type::Bytes_32: default: rtpLength = GSM_RTPFRAME_SIZE_33;
return GSM_RTPFRAME_SIZE_32;
case Type::Bytes_33:
return GSM_RTPFRAME_SIZE_33;
case Type::Bytes_65:
return GSM_RTPFRAME_SIZE_32 + GSM_RTPFRAME_SIZE_33;
} }
return GSM_RTPFRAME_SIZE_33; return {
.mName = "GSM-06.10",
.mSamplerate = 8000,
.mChannels = 1,
.mPcmLength = GSM_AUDIOFRAME_TIME * 16,
.mFrameTime = GSM_AUDIOFRAME_TIME,
.mRtpLength = rtpLength
};
} }
int GsmCodec::pcmLength() Codec::EncodeResult GsmCodec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{
return GSM_AUDIOFRAME_TIME * 16;
}
int GsmCodec::frameTime()
{
return GSM_AUDIOFRAME_TIME;
}
int GsmCodec::samplerate()
{
return 8000;
}
int GsmCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{ {
int outputBytes = 0; int outputBytes = 0;
char* outputBuffer = (char*)output.data();
char* outputBuffer = (char*)output; for (int i = 0; i < input.size_bytes() / pcmLength(); i++)
for (int i = 0; i < inputBytes/pcmLength(); i++)
{ {
gsm_encode(mGSM, (gsm_signal *)input+160*i, (gsm_byte*)outputBuffer); gsm_encode(mGSM, (gsm_signal *)input.data()+160*i, (gsm_byte*)outputBuffer);
outputBuffer += rtpLength(); outputBuffer += rtpLength();
outputBytes += rtpLength(); outputBytes += rtpLength();
} }
return outputBytes; return {.mEncoded = (size_t)outputBytes};
} }
int GsmCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) Codec::DecodeResult GsmCodec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
if (inputBytes % rtpLength() != 0) if (input.size_bytes() % rtpLength() != 0)
return 0; return {.mDecoded = 0};
int i=0; int i=0;
for (i = 0; i < inputBytes/rtpLength(); i++) for (i = 0; i < input.size_bytes() / rtpLength(); i++)
gsm_decode(mGSM, (gsm_byte *)input + 33 * i, (gsm_signal *)output + 160 * i); gsm_decode(mGSM, (gsm_byte *)input.data() + 33 * i, (gsm_signal *)output.data() + 160 * i);
return i * 320; return {.mDecoded = (size_t)i * 320};
} }
int GsmCodec::plc(int lostFrames, void* output, int outputCapacity) size_t GsmCodec::plc(int lostFrames, std::span<uint8_t> output)
{ {
if (outputCapacity < lostFrames * pcmLength()) if (output.size_bytes() < lostFrames * pcmLength())
return 0; return 0;
// Return silence frames // Return silence frames
memset(output, 0, lostFrames * pcmLength()); memset(output.data(), 0, lostFrames * pcmLength());
return lostFrames * pcmLength(); return lostFrames * pcmLength();
} }
@@ -1155,58 +1049,52 @@ G722Codec::G722Codec()
G722Codec::~G722Codec() G722Codec::~G722Codec()
{ {
g722_decode_release((g722_decode_state_t*)mDecoder); g722_decode_release((g722_decode_state_t*)mDecoder); mDecoder = nullptr;
g722_encode_release((g722_encode_state_t*)mEncoder); g722_encode_release((g722_encode_state_t*)mEncoder); mEncoder = nullptr;
} }
const char* G722Codec::name() Codec::Info G722Codec::info() {
{ // ToDo: double check the G722 calls - remember RFC has bug about samplerate
return G722_MIME_NAME; return {
.mName = G722_MIME_NAME,
.mSamplerate = 8000,
.mChannels = 1,
.mPcmLength = 640,
.mFrameTime = 20,
.mRtpLength = 160
};
} }
int G722Codec::pcmLength() Codec::EncodeResult G722Codec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
return 640; if (output.size_bytes() < input.size_bytes() / 4)
return {.mEncoded = 0}; // Destination buffer not big enough
int r = g722_encode((g722_encode_state_t *)mEncoder, (unsigned char*)output.data(), ( short*)input.data(), input.size_bytes() / 2);
if (r < 0)
return {.mEncoded = 0};
return {.mEncoded = (size_t)r};
} }
int G722Codec::frameTime() Codec::DecodeResult G722Codec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
return 20; if (output.size_bytes() < input.size_bytes() * 4)
return {.mDecoded = 0}; // Destination buffer not big enough
int r = g722_decode((g722_decode_state_t *)mDecoder, (short*)output.data(), (unsigned char*)input.data(), input.size_bytes()) * 2;
if (r < 0)
return {.mDecoded = 0};
return {.mDecoded = (size_t)r};
} }
int G722Codec::rtpLength() size_t G722Codec::plc(int lostFrames, std::span<uint8_t> output)
{ {
return 160; if (output.size_bytes() < lostFrames * pcmLength())
}
int G722Codec::samplerate()
{
return 8000;
}
int G722Codec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{
if (outputCapacity < inputBytes / 4)
return 0; // Destination buffer not big enough
return g722_encode((g722_encode_state_t *)mEncoder, (unsigned char*)output, ( short*)input, inputBytes / 2);
}
int G722Codec::decode(const void* input, int inputBytes, void* output, int outputCapacity)
{
if (outputCapacity < inputBytes * 4)
return 0; // Destination buffer not big enough
return g722_decode((g722_decode_state_t *)mDecoder, ( short*)output, (unsigned char*)input, inputBytes) * 2;
}
int G722Codec::plc(int lostFrames, void* output, int outputCapacity)
{
if (outputCapacity < lostFrames * pcmLength())
return 0; return 0;
// Return silence frames // Return silence frames
memset(output, 0, lostFrames * pcmLength()); memset(output.data(), 0, lostFrames * pcmLength());
return lostFrames * pcmLength(); return lostFrames * pcmLength();
} }
@@ -1318,7 +1206,6 @@ static bool repackHalfRate(BitReader& br, uint16_t frame[22], bool& lastItem)
} }
GsmHrCodec::GsmHrCodec() GsmHrCodec::GsmHrCodec()
:mDecoder(nullptr)
{ {
mDecoder = new GsmHr::Codec(); mDecoder = new GsmHr::Codec();
} }
@@ -1329,34 +1216,21 @@ GsmHrCodec::~GsmHrCodec()
mDecoder = nullptr; mDecoder = nullptr;
} }
const char* GsmHrCodec::name() Codec::Info GsmHrCodec::info() {
{ return {
return "GSM-HR-08"; .mName = "GSM-HR-08",
.mSamplerate = 8000,
.mChannels = 1,
.mPcmLength = 20 * 8 * 2,
.mFrameTime = 20,
.mRtpLength = 0
};
} }
int GsmHrCodec::pcmLength() Codec::EncodeResult GsmHrCodec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
return frameTime() * 8 * 2; // Not supported yet
} return {.mEncoded = 0};
int GsmHrCodec::rtpLength()
{
return 0;
}
int GsmHrCodec::frameTime()
{
return 20;
}
int GsmHrCodec::samplerate()
{
return 8000;
}
int GsmHrCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{
return 0;
} }
static const int params_unvoiced[] = { static const int params_unvoiced[] = {
@@ -1447,23 +1321,23 @@ hr_ref_from_canon(uint16_t *hr_ref, const uint8_t *canon)
[+] PQ: Adding conversion from canon to rawpcm-s16le (for codec pcm) [+] PQ: Adding conversion from canon to rawpcm-s16le (for codec pcm)
[+] PQ: Adding file output (blk_len=320) [+] PQ: Adding file output (blk_len=320)
*/ */
int GsmHrCodec::decode(const void* input, int inputBytes, void* output, int outputCapacity) Codec::DecodeResult GsmHrCodec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
ByteBuffer bb(input, inputBytes, ByteBuffer::CopyBehavior::UseExternal); ByteBuffer bb(input, ByteBuffer::CopyBehavior::UseExternal);
BitReader br(bb); BitReader br(bb);
uint16_t hr_ref[22]; uint16_t hr_ref[22];
hr_ref_from_canon(hr_ref, (const uint8_t*)input + 1); hr_ref_from_canon(hr_ref, input.data() + 1);
hr_ref[18] = 0; /* BFI : 1 bit */ hr_ref[18] = 0; /* BFI : 1 bit */
hr_ref[19] = 0; /* UFI : 1 bit */ hr_ref[19] = 0; /* UFI : 1 bit */
hr_ref[20] = 0; /* SID : 2 bit */ hr_ref[20] = 0; /* SID : 2 bit */
hr_ref[21] = 0; /* TAF : 1 bit */ hr_ref[21] = 0; /* TAF : 1 bit */
reinterpret_cast<GsmHr::Codec*>(mDecoder)->speechDecoder((int16_t*)hr_ref, (int16_t*)output); reinterpret_cast<GsmHr::Codec*>(mDecoder)->speechDecoder((int16_t*)hr_ref, (int16_t*)output.data());
return 320; return {.mDecoded = 320};
} }
int GsmHrCodec::plc(int lostFrames, void* output, int outputCapacity) size_t GsmHrCodec::plc(int lostFrames, std::span<uint8_t> output)
{ {
return 0; return 0;
} }

View File

@@ -40,42 +40,41 @@ public:
{ {
public: public:
const char* name() override; const char* name() override;
int channels() override; int channels() override;
int samplerate() override; int samplerate() override;
int payloadType() override; int payloadType() override;
void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; void updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override; int processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) override;
PCodec create() override; PCodec create() override;
}; };
G729Codec(); G729Codec();
~G729Codec() override; ~G729Codec() override;
const char* name() override; Info info() override;
int pcmLength() override;
int rtpLength() override; EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int frameTime() override; DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int samplerate() override; size_t plc(int lostFrames, std::span<uint8_t> output) override;
int channels() override;
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int plc(int lostFrames, void* output, int outputCapacity) override;
}; };
class OpusCodec: public Codec class OpusCodec: public Codec
{ {
protected: protected:
OpusEncoder *mEncoderCtx; OpusEncoder *mEncoderCtx = nullptr;
OpusDecoder *mDecoderCtx; OpusDecoder *mDecoderCtx = nullptr;
int mPTime, mSamplerate, mChannels; int mPTime = 0, mSamplerate = 0, mChannels = 0;
// Audio::SpeexResampler mDecodeResampler; int mDecoderChannels = 0;
int mDecoderChannels;
public: public:
struct Params struct Params
{ {
bool mUseDtx, mUseInbandFec, mStereo; bool mUseDtx = false,
int mPtime, mTargetBitrate, mExpectedPacketLoss; mUseInbandFec = false,
mStereo = false;
int mPtime = 0,
mTargetBitrate = 0,
mExpectedPacketLoss = 0;
Params(); Params();
resip::Data toString() const; resip::Data toString() const;
@@ -102,28 +101,24 @@ public:
PCodec create() override; PCodec create() override;
}; };
OpusCodec(int samplerate, int channels, int ptime); OpusCodec(Audio::Format fmt, int ptime);
~OpusCodec(); ~OpusCodec();
void applyParams(const Params& params); void applyParams(const Params& params);
const char* name(); Info info() override;
int pcmLength();
int rtpLength(); EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int frameTime(); DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int samplerate(); size_t plc(int lostFrames, std::span<uint8_t> output) override;
int channels();
int encode(const void* input, int inputBytes, void* output, int outputCapacity);
int decode(const void* input, int inputBytes, void* output, int outputCapacity);
int plc(int lostFrames, void* output, int outputCapacity);
}; };
class IlbcCodec: public Codec class IlbcCodec: public Codec
{ {
protected: protected:
int mPacketTime; /// Single frame time (20 or 30 ms) int mPacketTime = 0; /// Single frame time (20 or 30 ms)
iLBC_encinst_t* mEncoderCtx; iLBC_encinst_t* mEncoderCtx = nullptr;
iLBC_decinst_t* mDecoderCtx; iLBC_decinst_t* mDecoderCtx = nullptr;
public: public:
class IlbcFactory: public Factory class IlbcFactory: public Factory
@@ -146,14 +141,11 @@ public:
IlbcCodec(int packetTime); IlbcCodec(int packetTime);
virtual ~IlbcCodec(); virtual ~IlbcCodec();
const char* name(); Info info() override;
int pcmLength();
int rtpLength(); EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int frameTime(); DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int samplerate(); size_t plc(int lostFrames, std::span<uint8_t> output) override;
int encode(const void* input, int inputBytes, void* output, int outputCapacity);
int decode(const void* input, int inputBytes, void* output, int outputCapacity);
int plc(int lostFrames, void* output, int outputCapacity);
}; };
class G711Codec: public Codec class G711Codec: public Codec
@@ -186,15 +178,11 @@ public:
G711Codec(int type); G711Codec(int type);
~G711Codec(); ~G711Codec();
const char* name(); Info info() override;
int pcmLength();
int frameTime();
int rtpLength();
int samplerate();
int encode(const void* input, int inputBytes, void* output, int outputCapacity); EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int decode(const void* input, int inputBytes, void* output, int outputCapacity); DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int plc(int lostSamples, void* output, int outputCapacity); size_t plc(int lostSamples, std::span<uint8_t> output) override ;
protected: protected:
int mType; /// Determines if it is u-law or a-law codec. Its value is ALaw or ULaw. int mType; /// Determines if it is u-law or a-law codec. Its value is ALaw or ULaw.
@@ -203,9 +191,9 @@ protected:
class IsacCodec: public Codec class IsacCodec: public Codec
{ {
protected: protected:
int mSamplerate; int mSamplerate = 0;
ISACFIX_MainStruct* mEncoderCtx; ISACFIX_MainStruct* mEncoderCtx = nullptr;
ISACFIX_MainStruct* mDecoderCtx; ISACFIX_MainStruct* mDecoderCtx = nullptr;
public: public:
class IsacFactory16K: public Factory class IsacFactory16K: public Factory
{ {
@@ -237,15 +225,11 @@ public:
IsacCodec(int sampleRate); IsacCodec(int sampleRate);
~IsacCodec(); ~IsacCodec();
const char* name(); Info info() override;
int pcmLength();
int rtpLength();
int frameTime();
int samplerate();
int encode(const void* input, int inputBytes, void* output, int outputCapacity); EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int decode(const void* input, int inputBytes, void* output, int outputCapacity); DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int plc(int lostFrames, void* output, int outputCapacity); size_t plc(int lostFrames, std::span<uint8_t> output) override;
}; };
@@ -309,17 +293,13 @@ public:
GsmCodec(Type codecType); GsmCodec(Type codecType);
/*! Destructor. */ /*! Destructor. */
virtual ~GsmCodec(); ~GsmCodec();
const char* name(); Info info() override;
int pcmLength();
int rtpLength();
int frameTime();
int samplerate();
int encode(const void* input, int inputBytes, void* output, int outputCapacity); EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int decode(const void* input, int inputBytes, void* output, int outputCapacity); DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int plc(int lostFrames, void* output, int outputCapacity); size_t plc(int lostFrames, std::span<uint8_t> output) override;
}; };
/// GSM MIME name /// GSM MIME name
@@ -358,25 +338,19 @@ public:
PCodec create(); PCodec create();
}; };
G722Codec(); G722Codec();
virtual ~G722Codec(); ~G722Codec();
const char* name(); Info info() override;
int pcmLength();
int rtpLength();
int frameTime();
int samplerate();
int encode(const void* input, int inputBytes, void* output, int outputCapacity); EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int decode(const void* input, int inputBytes, void* output, int outputCapacity); DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int plc(int lostFrames, void* output, int outputCapacity); size_t plc(int lostFrames, std::span<uint8_t> output) override;
//unsigned GetSamplerate() { return 16000; }
}; };
class GsmHrCodec: public Codec class GsmHrCodec: public Codec
{ {
protected: protected:
void* mDecoder; void* mDecoder = nullptr;
public: public:
class GsmHrFactory: public Factory class GsmHrFactory: public Factory
@@ -396,15 +370,11 @@ public:
GsmHrCodec(); GsmHrCodec();
~GsmHrCodec() override; ~GsmHrCodec() override;
const char* name() override; Info info() override;
int pcmLength() override;
int rtpLength() override;
int frameTime() override;
int samplerate() override;
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override; EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override; DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int plc(int lostFrames, void* output, int outputCapacity) override; size_t plc(int lostFrames, std::span<uint8_t> output) override;
}; };
} }

View File

@@ -11,6 +11,7 @@
#include "MT_AudioReceiver.h" #include "MT_AudioReceiver.h"
#include "MT_AudioCodec.h" #include "MT_AudioCodec.h"
#include "MT_CngHelper.h" #include "MT_CngHelper.h"
#include "MT_Dtmf.h"
#include "../helper/HL_Log.h" #include "../helper/HL_Log.h"
#include "../helper/HL_Time.h" #include "../helper/HL_Time.h"
#include "../audio/Audio_Interface.h" #include "../audio/Audio_Interface.h"
@@ -115,7 +116,7 @@ bool SequenceSort(const std::shared_ptr<RtpBuffer::Packet>& p1, const std::share
return p1->rtp()->GetExtendedSequenceNumber() < p2->rtp()->GetExtendedSequenceNumber(); return p1->rtp()->GetExtendedSequenceNumber() < p2->rtp()->GetExtendedSequenceNumber();
} }
std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(std::shared_ptr<jrtplib::RTPPacket> packet, std::chrono::milliseconds timelength, int rate) std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(const std::shared_ptr<jrtplib::RTPPacket>& packet, std::chrono::milliseconds timelength, int rate)
{ {
if (!packet) if (!packet)
return std::shared_ptr<Packet>(); return std::shared_ptr<Packet>();
@@ -191,24 +192,24 @@ std::shared_ptr<RtpBuffer::Packet> RtpBuffer::add(std::shared_ptr<jrtplib::RTPPa
return std::shared_ptr<Packet>(); return std::shared_ptr<Packet>();
} }
RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl) RtpBuffer::FetchResult RtpBuffer::fetch()
{ {
Lock l(mGuard); Lock l(mGuard);
FetchResult result = FetchResult::NoPacket; FetchResult result;
rl.clear();
// See if there is enough information in buffer // See if there is enough information in buffer
auto total = findTimelength(); auto total = findTimelength();
while (total > mHigh && mPacketList.size() && 0ms != mHigh) while (total > mHigh && mPacketList.size() > 1 && 0ms != mHigh)
{ {
ICELogMedia( << "Dropping RTP packets from jitter buffer"); 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 // Save it as last packet however - to not confuse loss packet counter
mFetchedPacket = mPacketList.front(); mFetchedPacket = mPacketList.front();
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber(); mLastSeqno = mFetchedPacket->rtp()->GetExtendedSequenceNumber();
mLastReceiveTime = mFetchedPacket->rtp()->GetReceiveTime();
// Erase from packet list // Erase from packet list
mPacketList.erase(mPacketList.begin()); mPacketList.erase(mPacketList.begin());
@@ -217,10 +218,10 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
mStat.mPacketDropped++; mStat.mPacketDropped++;
} }
if (total < mLow) if (total < mLow || total == 0ms)
{ {
// Still not prebuffered // Still not prebuffered
result = FetchResult::NoPacket; result = {FetchResult::Status::NoPacket};
} }
else else
{ {
@@ -228,8 +229,8 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
{ {
if (mPacketList.empty()) if (mPacketList.empty())
{ {
result = FetchResult::NoPacket;
// Don't increase counter of lost packets here; maybe it is DTX // Don't increase counter of lost packets here; maybe it is DTX
result = {FetchResult::Status::NoPacket};
} }
else else
{ {
@@ -237,34 +238,39 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
auto& packet = *mPacketList.front(); auto& packet = *mPacketList.front();
uint32_t seqno = packet.rtp()->GetExtendedSequenceNumber(); uint32_t seqno = packet.rtp()->GetExtendedSequenceNumber();
// Gap between new packet and previous on // Gap between new packet and previous on
int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1; int gap = (int64_t)seqno - (int64_t)*mLastSeqno - 1;
gap = std::min(gap, 127);
if (gap > 0) if (gap > 0)
{ {
// std::cout << "Increase the packet loss for SSRC " << std::hex << mSsrc << std::endl; // std::cout << "Increase the packet loss for SSRC " << std::hex << mSsrc << std::endl;
mStat.mPacketLoss++; mStat.mPacketLoss += gap;
auto currentTimestamp = std::chrono::microseconds(uint64_t(packet.rtp()->GetReceiveTime().GetDouble() * 1000000));
// Report is the onetime; there is no many sequential 1-packet gap reports
if (mStat.mPacketLossTimeline.empty() || (mStat.mPacketLossTimeline.back().mEndSeqno != seqno)) if (mStat.mPacketLossTimeline.empty() || (mStat.mPacketLossTimeline.back().mEndSeqno != seqno))
mStat.mPacketLossTimeline.push_back({.mStartSeqno = *mLastSeqno, {
.mEndSeqno = seqno, auto gapStart = RtpHelper::toMicroseconds(*mLastReceiveTime);
.mGap = gap, auto gapEnd = RtpHelper::toMicroseconds(packet.rtp()->GetReceiveTime());
.mTimestamp = currentTimestamp}); mStat.mPacketLossTimeline.emplace_back(PacketLossEvent{.mStartSeqno = *mLastSeqno,
.mEndSeqno = seqno,
.mGap = gap,
.mTimestampStart = gapStart,
.mTimestampEnd = gapEnd});
}
mLastSeqno = *mLastSeqno + 1; // As we deal with the audio gap - return the silence and increase last seqno // ToDo: here we should decide smth - 2-packet gap shoud report Status::Gap two times at least; but current implementation gives only one.
// It is not big problem - as gap is detected when we have smth to return usually
result = FetchResult::Gap; mLastSeqno = seqno;
mLastReceiveTime = packet.rtp()->GetReceiveTime();
result = {FetchResult::Status::Gap};
} }
else else
{ {
result = FetchResult::RegularPacket; result = {FetchResult::Status::RegularPacket, mPacketList.front()};
rl.push_back(mPacketList.front());
// Save last returned normal packet // Save last returned normal packet
mFetchedPacket = mPacketList.front(); mFetchedPacket = result.mPacket;
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber(); mLastSeqno = result.mPacket->rtp()->GetExtendedSequenceNumber();
mLastReceiveTime = result.mPacket->rtp()->GetReceiveTime();
// Remove returned packet from the list // Remove returned packet from the list
mPacketList.erase(mPacketList.begin()); mPacketList.erase(mPacketList.begin());
@@ -277,14 +283,12 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
if (findTimelength() >= mPrebuffer && !mPacketList.empty()) if (findTimelength() >= mPrebuffer && !mPacketList.empty())
{ {
// Normal packet will be returned // Normal packet will be returned
result = FetchResult::RegularPacket; result = {FetchResult::Status::RegularPacket, mPacketList.front()};
// Put it to output list
rl.push_back(mPacketList.front());
// Remember returned packet // Remember returned packet
mFetchedPacket = mPacketList.front(); mFetchedPacket = result.mPacket;
mLastSeqno = mPacketList.front()->rtp()->GetExtendedSequenceNumber(); mLastSeqno = result.mPacket->rtp()->GetExtendedSequenceNumber();
mLastReceiveTime = result.mPacket->rtp()->GetReceiveTime();
// Remove returned packet from buffer list // Remove returned packet from buffer list
mPacketList.erase(mPacketList.begin()); mPacketList.erase(mPacketList.begin());
@@ -292,12 +296,12 @@ RtpBuffer::FetchResult RtpBuffer::fetch(ResultList& rl)
else else
{ {
ICELogMedia(<< "Jitter buffer was not prebuffered yet; resulting no packet"); ICELogMedia(<< "Jitter buffer was not prebuffered yet; resulting no packet");
result = FetchResult::NoPacket; result = {FetchResult::Status::NoPacket};
} }
} }
} }
if (result != FetchResult::NoPacket) if (result.mStatus != FetchResult::Status::NoPacket)
mReturnedCounter++; mReturnedCounter++;
return result; return result;
@@ -333,8 +337,7 @@ Receiver::~Receiver()
//-------------- AudioReceiver ---------------- //-------------- AudioReceiver ----------------
AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat) AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics &stat)
:Receiver(stat), mBuffer(stat), mCodecSettings(settings), :Receiver(stat), mBuffer(stat), mDtmfBuffer(stat), mCodecSettings(settings), mCodecList(settings), mDtmfReceiver(stat)
mCodecList(settings)
{ {
// Init resamplers // Init resamplers
mResampler8.start(AUDIO_CHANNELS, 8000, AUDIO_SAMPLERATE); mResampler8.start(AUDIO_CHANNELS, 8000, AUDIO_SAMPLERATE);
@@ -346,6 +349,12 @@ AudioReceiver::AudioReceiver(const CodecList::Settings& settings, MT::Statistics
mCodecList.setSettings(settings); mCodecList.setSettings(settings);
mCodecList.fillCodecMap(mCodecMap); mCodecList.fillCodecMap(mCodecMap);
mAvailable.setCapacity(AUDIO_SAMPLERATE * sizeof(short));
mDtmfBuffer.setPrebuffer(0ms);
mDtmfBuffer.setLow(0ms);
mDtmfBuffer.setHigh(1ms);
#if defined(DUMP_DECODED) #if defined(DUMP_DECODED)
mDecodedDump = std::make_shared<Audio::WavFileWriter>(); mDecodedDump = std::make_shared<Audio::WavFileWriter>();
mDecodedDump->open("decoded.wav", 8000 /*G711*/, AUDIO_CHANNELS); mDecodedDump->open("decoded.wav", 8000 /*G711*/, AUDIO_CHANNELS);
@@ -401,12 +410,10 @@ size_t decode_packet(Codec& codec, RTPPacket& p, void* output_buffer, size_t out
for (int i=0; i < frame_count; i++) for (int i=0; i < frame_count; i++)
{ {
auto decoded_length = codec.decode(p.GetPayloadData() + i * codec.rtpLength(), auto r = codec.decode({p.GetPayloadData() + i * codec.rtpLength(), (size_t)frame_length},
frame_length, {(uint8_t*)output_buffer, output_capacity});
output_buffer,
output_capacity);
result += decoded_length; result += r.mDecoded;
} }
} }
else else
@@ -428,66 +435,72 @@ bool AudioReceiver::add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** de
// Increase codec counter // Increase codec counter
mStat.mCodecCount[ptype]++; mStat.mCodecCount[ptype]++;
// Check if codec can be handled // Check if we deal with telephone-event
Codec* codec = nullptr; if (p->GetPayloadType() == mCodecSettings.mTelephoneEvent)
auto codecIter = mCodecMap.find(ptype);
if (codecIter == mCodecMap.end())
{ {
// Well, there is no information about the codec; skip this packet *detectedCodec = nullptr;
mDtmfBuffer.add(p, 10ms, 8000);
} }
else else
{ {
// Check if codec is creating lazily // Look for codec
if (!codecIter->second) // Check if codec can be handled
Codec* codec = nullptr;
auto codecIter = mCodecMap.find(ptype);
if (codecIter != mCodecMap.end())
{ {
codecIter->second = mCodecList.createCodecByPayloadType(ptype); // Check if codec is creating lazily
if (!codecIter->second)
{
codecIter->second = mCodecList.createCodecByPayloadType(ptype);
}
codec = codecIter->second.get();
// Return pointer to codec if needed.get()
if (detectedCodec)
*detectedCodec = codec;
if (mStat.mCodecName.empty() && codec)
mStat.mCodecName = codec->name();
if (!codec)
time_length = 10;
else
if (!codec->rtpLength())
time_length = codec->frameTime();
else
time_length = lround(double(payloadLength) / codec->rtpLength() * codec->frameTime());
if (codec)
samplerate = codec->samplerate();
} }
codec = codecIter->second.get();
// Return pointer to codec if needed.get()
if (detectedCodec)
*detectedCodec = codec;
if (mStat.mCodecName.empty() && codec)
mStat.mCodecName = codec->name();
// Process jitter anyway - can we decode payload or not
mJitterStats.process(p.get(), samplerate);
mStat.mJitter = static_cast<float>(mJitterStats.get());
if (!codec) if (!codec)
time_length = 10; return false; // There is no sense to add this packet into jitter buffer - we can't decode this
// Check if packet is CNG
if (payloadLength >= 1 && payloadLength <= 6 && (ptype == 0 || ptype == 8))
time_length = mLastPacketTimeLength ? mLastPacketTimeLength : 20;
else else
if (!codec->rtpLength()) // Check if packet is too short from time length side
time_length = codec->frameTime(); if (time_length < 2)
else {
time_length = lround(double(payloadLength) / codec->rtpLength() * codec->frameTime()); // It will cause statistics to report about bad RTP packet
// I have to replay last packet payload here to avoid report about lost packet
mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate);
return false;
}
if (codec) // Queue packet to buffer
samplerate = codec->samplerate(); auto packet = mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate).get();
return packet;
} }
return {};
// Process jitter
mJitterStats.process(p.get(), samplerate);
mStat.mJitter = static_cast<float>(mJitterStats.get());
if (!codec)
return false; // There is no sense to add this packet into jitter buffer - we can't decode this
// Check if packet is CNG
if (payloadLength >= 1 && payloadLength <= 6 && (ptype == 0 || ptype == 8))
time_length = mLastPacketTimeLength ? mLastPacketTimeLength : 20;
else
// Check if packet is too short from time length side
if (time_length < 2)
{
// It will cause statistics to report about bad RTP packet
// I have to replay last packet payload here to avoid report about lost packet
mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate);
return false;
}
// Queue packet to buffer
auto packet = mBuffer.add(p, std::chrono::milliseconds(time_length), samplerate).get();
return packet;
} }
void AudioReceiver::processDecoded(Audio::DataWindow& output, DecodeOptions options) void AudioReceiver::processDecoded(Audio::DataWindow& output, DecodeOptions options)
@@ -553,17 +566,21 @@ void AudioReceiver::produceCNG(std::chrono::milliseconds length, Audio::DataWind
} }
} }
AudioReceiver::DecodeResult AudioReceiver::decodeGap(Audio::DataWindow& output, DecodeOptions options) AudioReceiver::DecodeResult AudioReceiver::decodeGapTo(Audio::DataWindow& output, DecodeOptions options)
{ {
ICELogDebug(<< "Gap detected."); ICELogDebug(<< "Gap detected.");
mDecodedLength = mResampledLength = 0; mDecodedLength = mResampledLength = 0;
if (mCngPacket && mCodec) if (mCngPacket && mCodec)
{ {
// Synthesize comfort noise. It will be done on AUDIO_SAMPLERATE rate directly to mResampledFrame buffer. if (mCngPacket->rtp()->GetPayloadType() == 13)
// Do not forget to send this noise to analysis {
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, // Synthesize comfort noise. It will be done on AUDIO_SAMPLERATE rate directly to mResampledFrame buffer.
reinterpret_cast<short*>(mDecodedFrame), false); // Do not forget to send this noise to analysis
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, reinterpret_cast<short*>(mDecodedFrame), false);
}
else
decodePacketTo(output, options, mCngPacket);
} }
else else
if (mCodec && mFrameCount && !mCodecSettings.mSkipDecode) if (mCodec && mFrameCount && !mCodecSettings.mSkipDecode)
@@ -573,7 +590,7 @@ AudioReceiver::DecodeResult AudioReceiver::decodeGap(Audio::DataWindow& output,
mDecodedLength = 0; mDecodedLength = 0;
else else
{ {
mDecodedLength = mCodec->plc(mFrameCount, mDecodedFrame, sizeof mDecodedFrame); mDecodedLength = mCodec->plc(mFrameCount, {(uint8_t*)mDecodedFrame, sizeof mDecodedFrame});
if (!mDecodedLength) if (!mDecodedLength)
{ {
// PLC is not support or failed // PLC is not support or failed
@@ -588,158 +605,258 @@ AudioReceiver::DecodeResult AudioReceiver::decodeGap(Audio::DataWindow& output,
if (mDecodedLength) if (mDecodedLength)
{ {
processDecoded(output, options); processDecoded(output, options);
return DecodeResult_Ok; return {.mStatus = DecodeResult::Status::Ok, .mSamplerate = mCodec->samplerate(), .mChannels = mCodec->channels()};
} }
else else
return DecodeResult_Skip; return {.mStatus = DecodeResult::Status::Skip};
} }
AudioReceiver::DecodeResult AudioReceiver::decodePacket(const RtpBuffer::ResultList& rl, Audio::DataWindow& output, DecodeOptions options, int* rate) AudioReceiver::DecodeResult AudioReceiver::decodePacketTo(Audio::DataWindow& output, DecodeOptions options, const std::shared_ptr<RtpBuffer::Packet>& packet)
{ {
DecodeResult result = DecodeResult_Skip; if (!packet || !packet->rtp())
return {DecodeResult::Status::Skip};
DecodeResult result = {.mStatus = DecodeResult::Status::Skip};
auto& rtp = *packet->rtp(); // Syntax sugar
mFailedCount = 0; mFailedCount = 0;
for (const std::shared_ptr<RtpBuffer::Packet>& p: rl) // Check if we need to emit silence or CNG - previously CNG packet was detected. Emit CNG audio here if needed.
if (mLastPacketTimestamp && mLastPacketTimeLength && mCodec)
{ {
assert(p); int units = rtp.GetTimestamp() - *mLastPacketTimestamp;
// Check if we need to emit silence or CNG - previously CNG packet was detected. Emit CNG audio here if needed. int milliseconds = units / (mCodec->samplerate() / 1000);
if (mLastPacketTimestamp && mLastPacketTimeLength && mCodec) if (milliseconds > mLastPacketTimeLength)
{ {
int units = p->rtp()->GetTimestamp() - *mLastPacketTimestamp; auto silenceLength = std::chrono::milliseconds(milliseconds - mLastPacketTimeLength);
int milliseconds = units / (mCodec->samplerate() / 1000);
if (milliseconds > mLastPacketTimeLength)
{
auto silenceLength = std::chrono::milliseconds(milliseconds - mLastPacketTimeLength);
if (mCngPacket && options.mFillGapByCNG) if (mCngPacket && options.mFillGapByCNG)
produceCNG(silenceLength, output, options); produceCNG(silenceLength, output, options);
else else
produceSilence(silenceLength, output, options); produceSilence(silenceLength, output, options);
}
} }
}
mLastPacketTimestamp = p->rtp()->GetTimestamp(); mLastPacketTimestamp = rtp.GetTimestamp();
// Find codec by payload type // Find codec by payload type
int ptype = p->rtp()->GetPayloadType(); int ptype = rtp.GetPayloadType();
// Look into mCodecMap if exists // Look into mCodecMap if exists
auto codecIter = mCodecMap.find(ptype); auto codecIter = mCodecMap.find(ptype);
if (codecIter == mCodecMap.end()) if (codecIter == mCodecMap.end())
return {}; return {};
if (!codecIter->second)
codecIter->second = mCodecList.createCodecByPayloadType(ptype);
if (!codecIter->second) mCodec = codecIter->second;
codecIter->second = mCodecList.createCodecByPayloadType(ptype); if (mCodec)
{
result.mChannels = mCodec->channels();
result.mSamplerate = mCodec->samplerate();
mCodec = codecIter->second; // Check if it is CNG packet
if (mCodec) if (((ptype == 0 || ptype == 8) && rtp.GetPayloadLength() >= 1 && rtp.GetPayloadLength() <= 6) || rtp.GetPayloadType() == 13)
{ {
if (rate) if (options.mSkipDecode)
*rate = mCodec->samplerate(); mDecodedLength = 0;
else
// Check if it is CNG packet
if ((ptype == 0 || ptype == 8) && p->rtp()->GetPayloadLength() >= 1 && p->rtp()->GetPayloadLength() <= 6)
{ {
if (options.mSkipDecode) mCngPacket = packet;
mDecodedLength = 0; mCngDecoder.decode3389(rtp.GetPayloadData(), rtp.GetPayloadLength());
else
{
mCngPacket = p->rtp();
mCngDecoder.decode3389(p->rtp()->GetPayloadData(), p->rtp()->GetPayloadLength());
// Emit CNG mLastPacketLength milliseconds // Emit CNG mLastPacketLength milliseconds
mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, mDecodedLength = mCngDecoder.produce(mCodec->samplerate(), mLastPacketTimeLength, (short*)mDecodedFrame, true);
(short*)mDecodedFrame, true); if (mDecodedLength)
if (mDecodedLength) processDecoded(output, options);
processDecoded(output, options); }
result.mStatus = DecodeResult::Status::Ok;
}
else
{
// Reset CNG packet as we get regular RTP packet
mCngPacket.reset();
// Handle here regular RTP packets
// Check if payload length is ok
size_t payload_length = rtp.GetPayloadLength();
size_t rtp_frame_length = mCodec->rtpLength();
int tail = rtp_frame_length ? payload_length % rtp_frame_length : 0;
if (!tail)
{
// Find number of frames
mFrameCount = mCodec->rtpLength() ? rtp.GetPayloadLength() / mCodec->rtpLength() : 1;
int frameLength = mCodec->rtpLength() ? mCodec->rtpLength() : (int)rtp.GetPayloadLength();
// Save last packet time length
mLastPacketTimeLength = mFrameCount * mCodec->frameTime();
// Decode
for (int i=0; i<mFrameCount && !mCodecSettings.mSkipDecode; i++)
{
if (options.mSkipDecode)
mDecodedLength = 0;
else
{
// Decode frame by frame
auto codecInput = std::span{rtp.GetPayloadData() + i * mCodec->rtpLength(), (size_t)frameLength};
auto codecOutput = std::span{(uint8_t*)mDecodedFrame, sizeof mDecodedFrame};
auto r = mCodec->decode(codecInput, codecOutput);
mDecodedLength = r.mDecoded;
if (mDecodedLength > 0)
processDecoded(output, options);
// What is important - here we may have packet marked as CNG
if (r.mIsCng)
mCngPacket = packet;
}
} }
result = DecodeResult_Ok; result.mStatus = mFrameCount > 0 ? DecodeResult::Status::Ok : DecodeResult::Status::Skip;
// Check for bitrate counter
updateAmrCodecStats(mCodec.get());
} }
else else
{ {
// Reset CNG packet as we get regular RTP packet // RTP packet with tail - it should not happen
mCngPacket.reset(); result.mStatus = DecodeResult::Status::BadPacket;
// Handle here regular RTP packets
// Check if payload length is ok
size_t payload_length = p->rtp()->GetPayloadLength();
size_t rtp_frame_length = mCodec->rtpLength();
int tail = rtp_frame_length ? payload_length % rtp_frame_length : 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.mSkipDecode)
mDecodedLength = 0;
else
{
// Decode frame by frame
mDecodedLength = mCodec->decode(p->rtp()->GetPayloadData() + i * mCodec->rtpLength(),
frameLength, mDecodedFrame, sizeof mDecodedFrame);
if (mDecodedLength > 0)
processDecoded(output, options);
}
}
result = mFrameCount > 0 ? DecodeResult_Ok : DecodeResult_Skip;
// Check for bitrate counter
updateAmrCodecStats(mCodec.get());
}
else
{
// RTP packet with tail - it should not happen
result = DecodeResult_BadPacket;
}
} }
} }
} }
return result; return result;
} }
AudioReceiver::DecodeResult AudioReceiver::decodeNone(Audio::DataWindow& output, DecodeOptions options) AudioReceiver::DecodeResult AudioReceiver::decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options)
{ {
// ICELogDebug(<< "No packet available in jitter buffer"); // There are two cases
mFailedCount++; // First is we have no ready time estimated how much audio should be emitted i.e. audio is decoded right after the next packet arrives.
return DecodeResult_Skip; // In this case we just skip the analysis - we should not be called in this situation
} if (options.mElapsed == 0ms || !mCodec)
return {.mStatus = DecodeResult::Status::Skip};
AudioReceiver::DecodeResult AudioReceiver::getAudio(Audio::DataWindow& output, DecodeOptions options, int* rate) // No packet available at all (and no previous CNG packet) - so return the silence
{ if (options.mElapsed != 0ms && mCodec)
DecodeResult result = DecodeResult_Skip;
// Get next packet from buffer
RtpBuffer::ResultList rl;
RtpBuffer::FetchResult fr = mBuffer.fetch(rl);
switch (fr)
{ {
case RtpBuffer::FetchResult::Gap: result = decodeGap(output, options); break; Audio::Format fmt = options.mResampleToMainRate ? Audio::Format(AUDIO_SAMPLERATE, 1) : mCodec->getAudioFormat();
case RtpBuffer::FetchResult::NoPacket: result = decodeNone(output, options); break; if (mCngPacket)
case RtpBuffer::FetchResult::RegularPacket: result = decodePacket(rl, output, options, rate); break; {
default: // Try to decode it - replay previous audio decoded or use CNG decoder (if payload type is 13)
assert(0); if (mCngPacket->rtp()->GetPayloadType() == 13)
{
// Using latest CNG packet to produce comfort noise
auto produced = mCngDecoder.produce(fmt.rate(), options.mElapsed.count(), (short*)(output.data() + output.filled()), false);
output.setFilled(output.filled() + produced);
return {.mStatus = DecodeResult::Status::Ok, .mSamplerate = fmt.rate(), .mChannels = fmt.channels()};
}
else
{
// Here we have another packet marked as CNG - for another decoder
// Just decode it +1 time
return decodePacketTo(output, options, mCngPacket);
}
}
else
{
// Emit silence if codec information is available - it is to properly handle the gaps
auto avail = output.getTimeLength(fmt.rate(), fmt.channels());
if (options.mElapsed > avail)
output.addZero(fmt.sizeFromTime(options.mElapsed - avail));
}
} }
if (result == DecodeResult_Ok) mFailedCount++;
return {.mStatus = DecodeResult::Status::Skip};
}
AudioReceiver::DecodeResult AudioReceiver::getAudioTo(Audio::DataWindow& output, DecodeOptions options)
{
DecodeResult result = {.mStatus = DecodeResult::Status::Skip};
// Process RFC2833 here; it doesn't result in any audio - only callbacks and statistics
auto fr = mDtmfBuffer.fetch();
if (fr.mPacket && fr.mStatus == RtpBuffer::FetchResult::Status::RegularPacket)
mDtmfReceiver.add(fr.mPacket->rtp());
auto produced = 0ms;
if (mAvailable.filled() && mCodec && options.mElapsed != 0ms)
{
Audio::Format fmt = options.mResampleToMainRate ? Audio::Format(AUDIO_SAMPLERATE, 1) : mCodec->getAudioFormat();
auto initiallyAvailable = mCodec ? mAvailable.getTimeLength(fmt.rate(), fmt.channels()) : 0ms;
if (initiallyAvailable != 0ms)
{
std::chrono::milliseconds resultTime = std::min(initiallyAvailable, options.mElapsed);
auto resultLen = fmt.sizeFromTime(resultTime);
mAvailable.moveTo(output, resultLen);
produced += resultTime;
// Maybe request is satisfied ?
if (produced >= options.mElapsed)
return {.mStatus = DecodeResult::Status::Ok, .mSamplerate = fmt.rate(), .mChannels = fmt.channels()};
}
}
std::chrono::milliseconds decoded = 0ms;
do
{
// Get next packet from buffer
RtpBuffer::ResultList rl;
RtpBuffer::FetchResult fr = mBuffer.fetch();
// ICELogDebug(<< fr.toString() << " " << mBuffer.findTimelength());
switch (fr.mStatus)
{
case RtpBuffer::FetchResult::Status::Gap: result = decodeGapTo(mAvailable, options); break;
case RtpBuffer::FetchResult::Status::NoPacket: result = decodeEmptyTo(mAvailable, options); break;
case RtpBuffer::FetchResult::Status::RegularPacket: result = decodePacketTo(mAvailable, options, fr.mPacket); break;
default:
assert(0);
}
// Was there decoding at all ?
if (!mCodec)
break; // No sense to continue - we have no information at all
Audio::Format fmt = options.mResampleToMainRate ? Audio::Format(AUDIO_SAMPLERATE, 1) : mCodec->getAudioFormat();
result.mSamplerate = fmt.rate();
result.mChannels = fmt.channels();
// Have we anything interesting in the buffer ?
auto bufferAvailable = mAvailable.getTimeLength(fmt.rate(), fmt.channels());
if (bufferAvailable == 0ms)
break; // No sense to continue - decoding / CNG / PLC stopped totally
// How much data should be moved to result buffer ?
if (options.mElapsed != 0ms)
{
std::chrono::milliseconds resultTime = std::min(bufferAvailable, options.mElapsed - produced);
auto resultLen = fmt.sizeFromTime(resultTime);
mAvailable.moveTo(output, resultLen);
produced += resultTime;
}
else
mAvailable.moveTo(output, mAvailable.filled());
decoded += bufferAvailable;
}
while (produced < options.mElapsed);
if (produced != 0ms)
result.mStatus = DecodeResult::Status::Ok;
// Time statistics
if (result.mStatus == DecodeResult::Status::Ok)
{ {
// Decode statistics // Decode statistics
if (!mLastDecodeTimestamp) if (!mDecodeTimestamp)
mLastDecodeTimestamp = std::chrono::steady_clock::now(); mDecodeTimestamp = std::chrono::steady_clock::now();
else else
{ {
auto t = std::chrono::steady_clock::now(); auto t = std::chrono::steady_clock::now();
mStat.mDecodingInterval.process(std::chrono::duration_cast<std::chrono::milliseconds>(t - *mLastDecodeTimestamp).count()); mStat.mDecodingInterval.process(std::chrono::duration_cast<std::chrono::milliseconds>(t - *mDecodeTimestamp).count());
mLastDecodeTimestamp = t; mDecodeTimestamp = t;
} }
} }
return result; return result;
@@ -795,21 +912,26 @@ void AudioReceiver::updateAmrCodecStats(Codec* c)
AmrWbCodec* wb = dynamic_cast<AmrWbCodec*>(c); AmrWbCodec* wb = dynamic_cast<AmrWbCodec*>(c);
if (nb != nullptr) if (nb != nullptr)
{
mStat.mBitrateSwitchCounter = nb->getSwitchCounter(); mStat.mBitrateSwitchCounter = nb->getSwitchCounter();
mStat.mCng = nb->getCngCounter();
}
else else
if (wb != nullptr) if (wb != nullptr)
{
mStat.mBitrateSwitchCounter = wb->getSwitchCounter(); mStat.mBitrateSwitchCounter = wb->getSwitchCounter();
mStat.mCng = wb->getCngCounter();
}
#endif #endif
} }
int AudioReceiver::getSize() const int AudioReceiver::getSize() const
{ {
int result = 0; int result = 0;
result += sizeof(*this) + mResampler8.getSize() + mResampler16.getSize() + mResampler32.getSize() result += sizeof(*this) + mResampler8.getSize() + mResampler16.getSize() + mResampler32.getSize() + mResampler48.getSize();
+ mResampler48.getSize();
if (mCodec) if (mCodec)
result += mCodec->getSize(); ; // ToDo: need the way to calculate size of codec instances
return result; return result;
} }
@@ -860,5 +982,24 @@ DtmfReceiver::DtmfReceiver(Statistics& stat)
DtmfReceiver::~DtmfReceiver() DtmfReceiver::~DtmfReceiver()
{} {}
void DtmfReceiver::add(std::shared_ptr<RTPPacket> /*p*/) void DtmfReceiver::add(const std::shared_ptr<RTPPacket>& p)
{} {
auto ev = DtmfBuilder::parseRfc2833({p->GetPayloadData(), p->GetPayloadLength()});
if (ev.mTone != mEvent || ev.mEnd != mEventEnded)
{
if (!(mEvent == ev.mTone && !mEventEnded && ev.mEnd))
{
// New tone is here
if (mCallback)
mCallback(ev.mTone);
// Queue statistics item
mStat.mDtmf2833Timeline.emplace_back(Dtmf2833Event{.mTone = ev.mTone,
.mTimestamp = RtpHelper::toMicroseconds(p->GetReceiveTime())});
// Store to avoid triggering on the packet
mEvent = ev.mTone;
mEventEnded = ev.mEnd;
}
}
}

View File

@@ -28,13 +28,6 @@ using jrtplib::RTPPacket;
class RtpBuffer class RtpBuffer
{ {
public: public:
enum class FetchResult
{
RegularPacket,
Gap,
NoPacket
};
// Owns rtp packet data // Owns rtp packet data
class Packet class Packet
{ {
@@ -59,6 +52,29 @@ public:
std::chrono::microseconds mTimestamp = 0us; std::chrono::microseconds mTimestamp = 0us;
}; };
struct FetchResult
{
enum class Status
{
RegularPacket,
Gap,
NoPacket
};
Status mStatus = Status::NoPacket;
std::shared_ptr<Packet> mPacket;
std::string toString() const
{
switch (mStatus)
{
case Status::RegularPacket: return "packet";
case Status::Gap: return "gap";
case Status::NoPacket: return "empty";
}
}
};
RtpBuffer(Statistics& stat); RtpBuffer(Statistics& stat);
~RtpBuffer(); ~RtpBuffer();
@@ -81,12 +97,12 @@ public:
int getCount() const; int getCount() const;
// Returns false if packet was not add - maybe too old or too new or duplicate // 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, std::chrono::milliseconds timelength, int rate); std::shared_ptr<Packet> add(const std::shared_ptr<RTPPacket>& packet, std::chrono::milliseconds timelength, int rate);
typedef std::vector<std::shared_ptr<Packet>> ResultList; typedef std::vector<std::shared_ptr<Packet>> ResultList;
typedef std::shared_ptr<ResultList> PResultList; typedef std::shared_ptr<ResultList> PResultList;
FetchResult fetch(ResultList& rl); FetchResult fetch();
protected: protected:
unsigned mSsrc = 0; unsigned mSsrc = 0;
@@ -104,6 +120,7 @@ protected:
jrtplib::RTPSourceStats mRtpStats; jrtplib::RTPSourceStats mRtpStats;
std::shared_ptr<Packet> mFetchedPacket; std::shared_ptr<Packet> mFetchedPacket;
std::optional<uint32_t> mLastSeqno; std::optional<uint32_t> mLastSeqno;
std::optional<jrtplib::RTPTime> mLastReceiveTime;
// To calculate average interval between packet add. It is close to jitter but more useful in debugging. // To calculate average interval between packet add. It is close to jitter but more useful in debugging.
float mLastAddTime = 0.0f; float mLastAddTime = 0.0f;
@@ -119,6 +136,23 @@ protected:
Statistics& mStat; Statistics& mStat;
}; };
class DtmfReceiver: public Receiver
{
private:
char mEvent = 0;
bool mEventEnded = false;
std::chrono::milliseconds mEventStart = 0ms;
std::function<void(char)> mCallback;
public:
DtmfReceiver(Statistics& stat);
~DtmfReceiver();
void add(const std::shared_ptr<RTPPacket>& p);
void setCallback(std::function<void(char tone)> callback);
};
class AudioReceiver: public Receiver class AudioReceiver: public Receiver
{ {
public: public:
@@ -133,34 +167,33 @@ public:
// Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container). // Lifetime of pointer to codec is limited by lifetime of AudioReceiver (it is container).
bool add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec = nullptr); bool add(const std::shared_ptr<jrtplib::RTPPacket>& p, Codec** codec = nullptr);
// Returns false when there is no rtp data from jitter
/*enum DecodeOptions
{
DecodeOptions_ResampleToMainRate = 0,
DecodeOptions_DontResample = 1,
DecodeOptions_FillCngGap = 2,
DecodeOptions_SkipDecode = 4
};*/
struct DecodeOptions struct DecodeOptions
{ {
bool mResampleToMainRate = true; bool mResampleToMainRate = true; // Resample all decoded audio to AUDIO_SAMPLERATE
bool mFillGapByCNG = false; bool mFillGapByCNG = false; // Use CNG information if available
bool mSkipDecode = false; bool mSkipDecode = false; // Don't do decode, just dry run - fetch packets, remove them from the jitter buffer
std::chrono::milliseconds mElapsed = 0ms; // How much milliseconds should be decoded; zero value means "decode just next packet from the buffer"
}; };
enum DecodeResult struct DecodeResult
{ {
DecodeResult_Ok, // Decoded ok enum class Status
DecodeResult_Skip, // Just no data - emit silence instead {
DecodeResult_BadPacket // Error happened during the decode Ok, // Decoded ok
Skip, // Just no data - emit silence instead
BadPacket // Error happened during the decode
};
Status mStatus = Status::Ok;
int mSamplerate = 0;
int mChannels = 0;
}; };
DecodeResult getAudio(Audio::DataWindow& output, DecodeOptions options = {.mResampleToMainRate = true, .mFillGapByCNG = false, .mSkipDecode = false}, int* rate = nullptr); DecodeResult getAudioTo(Audio::DataWindow& output, DecodeOptions options);
// Looks for codec by payload type // Looks for codec by payload type
Codec* findCodec(int payloadType); Codec* findCodec(int payloadType);
RtpBuffer& getRtpBuffer() { return mBuffer; } RtpBuffer& getRtpBuffer() { return mBuffer; }
// Returns size of AudioReceiver's instance in bytes (including size of all data + codecs + etc.) // Returns size of AudioReceiver's instance in bytes (including size of all data + codecs + etc.)
int getSize() const; int getSize() const;
@@ -173,17 +206,23 @@ public:
protected: protected:
RtpBuffer mBuffer; // Jitter buffer itself RtpBuffer mBuffer; // Jitter buffer itself
RtpBuffer mDtmfBuffer; // These two (mDtmfBuffer / mDtmfReceiver) are for our analyzer stack only; in normal softphone logic DTMF packets goes via SingleAudioStream::mDtmfReceiver
DtmfReceiver mDtmfReceiver;
CodecMap mCodecMap; CodecMap mCodecMap;
PCodec mCodec; PCodec mCodec;
int mFrameCount = 0; int mFrameCount = 0;
CodecList::Settings mCodecSettings; CodecList::Settings mCodecSettings;
CodecList mCodecList; CodecList mCodecList;
JitterStatistics mJitterStats; JitterStatistics mJitterStats;
std::shared_ptr<jrtplib::RTPPacket> mCngPacket; std::shared_ptr<RtpBuffer::Packet> mCngPacket;
CngDecoder mCngDecoder; CngDecoder mCngDecoder;
size_t mDTXSamplesToEmit = 0; // How much silence (or CNG) should be emited before next RTP packet gets into the action size_t mDTXSamplesToEmit = 0; // How much silence (or CNG) should be emited before next RTP packet gets into the action
// Buffer to hold decoded data // Already decoded data that can be retrieved without actual decoding - it may happen because of getAudioTo() may be limited by time interval
Audio::DataWindow mAvailable;
// Temporary buffer to hold decoded data (it is better than allocate data on stack)
int16_t mDecodedFrame[MT_MAX_DECODEBUFFER]; int16_t mDecodedFrame[MT_MAX_DECODEBUFFER];
size_t mDecodedLength = 0; size_t mDecodedLength = 0;
@@ -200,11 +239,14 @@ protected:
std::optional<uint32_t> mLastPacketTimestamp; std::optional<uint32_t> mLastPacketTimestamp;
int mFailedCount = 0; int mFailedCount = 0;
Audio::Resampler mResampler8, mResampler16, mResampler32, mResampler48; Audio::Resampler mResampler8,
mResampler16,
mResampler32,
mResampler48;
Audio::PWavFileWriter mDecodedDump; Audio::PWavFileWriter mDecodedDump;
std::optional<std::chrono::steady_clock::time_point> mLastDecodeTimestamp; // Time last call happened to codec->decode() std::optional<std::chrono::steady_clock::time_point> mDecodeTimestamp; // Time last call happened to codec->decode()
float mIntervalSum = 0.0f; float mIntervalSum = 0.0f;
int mIntervalCount = 0; int mIntervalCount = 0;
@@ -220,19 +262,11 @@ protected:
// Calculate bitrate switch statistics for AMR codecs // Calculate bitrate switch statistics for AMR codecs
void updateAmrCodecStats(Codec* c); void updateAmrCodecStats(Codec* c);
DecodeResult decodeGap(Audio::DataWindow& output, DecodeOptions options); DecodeResult decodeGapTo(Audio::DataWindow& output, DecodeOptions options);
DecodeResult decodePacket(const RtpBuffer::ResultList& rl, Audio::DataWindow& output, DecodeOptions options, int* rate = nullptr); DecodeResult decodePacketTo(Audio::DataWindow& output, DecodeOptions options, const std::shared_ptr<RtpBuffer::Packet>& p);
DecodeResult decodeNone(Audio::DataWindow& output, DecodeOptions options); DecodeResult decodeEmptyTo(Audio::DataWindow& output, DecodeOptions options);
}; };
class DtmfReceiver: public Receiver
{
public:
DtmfReceiver(Statistics& stat);
~DtmfReceiver();
void add(std::shared_ptr<RTPPacket> p);
};
} }
#endif #endif

View File

@@ -210,18 +210,17 @@ void AudioStream::addData(const void* buffer, int bytes)
if (mSendingDump) if (mSendingDump)
mSendingDump->write((const char*)mCapturedAudio.data() + codec->pcmLength() * i, codec->pcmLength()); mSendingDump->write((const char*)mCapturedAudio.data() + codec->pcmLength() * i, codec->pcmLength());
int produced; auto r = codec->encode({(const uint8_t*)mCapturedAudio.data() + codec->pcmLength()*i, (size_t)codec->pcmLength()},
produced = codec->encode((const char*)mCapturedAudio.data() + codec->pcmLength()*i, {(uint8_t*)mFrameBuffer, MT_MAXAUDIOFRAME});
codec->pcmLength(), mFrameBuffer, MT_MAXAUDIOFRAME);
// Counter of processed input bytes of raw pcm data from microphone // Counter of processed input bytes of raw pcm data from microphone
processed += codec->pcmLength(); processed += codec->pcmLength();
encodedTime += codec->frameTime(); encodedTime += codec->frameTime();
mEncodedTime += codec->frameTime(); mEncodedTime += codec->frameTime();
if (produced) if (r.mEncoded)
{ {
mEncodedAudio.appendBuffer(mFrameBuffer, produced); mEncodedAudio.appendBuffer(mFrameBuffer, r.mEncoded);
if (packetTime <= encodedTime) if (packetTime <= encodedTime)
{ {
// Time to send packet // Time to send packet

View File

@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2014 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2026 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -9,31 +9,31 @@ using namespace MT;
int Codec::Factory::channels() int Codec::Factory::channels()
{ {
return 1; return 1;
} }
void Codec::Factory::create(CodecMap& codecs) void Codec::Factory::create(CodecMap& codecs)
{ {
codecs[payloadType()] = std::shared_ptr<Codec>(create()); codecs[payloadType()] = std::shared_ptr<Codec>(create());
} }
void Codec::Factory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) void Codec::Factory::updateSdp(resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {
codecs.push_back(resipCodec()); codecs.push_back(resipCodec());
} }
resip::Codec Codec::Factory::resipCodec() resip::Codec Codec::Factory::resipCodec()
{ {
resip::Codec c(this->name(), this->payloadType(), this->samplerate()); resip::Codec c(this->name(), this->payloadType(), this->samplerate());
return c; return c;
} }
int Codec::Factory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction) int Codec::Factory::processSdp(const resip::SdpContents::Session::Medium::CodecContainer& codecs, SdpDirection direction)
{ {
for (resip::SdpContents::Session::Medium::CodecContainer::const_iterator codecIter = codecs.begin(); codecIter != codecs.end(); ++codecIter) for (resip::SdpContents::Session::Medium::CodecContainer::const_iterator codecIter = codecs.begin(); codecIter != codecs.end(); ++codecIter)
{ {
if (resipCodec() == *codecIter) if (resipCodec() == *codecIter)
return codecIter->payloadType(); return codecIter->payloadType();
} }
return -1; return -1;
} }

View File

@@ -1,4 +1,4 @@
/* Copyright(C) 2007-2016 VoIP objects (voipobjects.com) /* Copyright(C) 2007-2026 VoIP objects (voipobjects.com)
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -6,11 +6,12 @@
#ifndef __MT_CODEC_H #ifndef __MT_CODEC_H
#define __MT_CODEC_H #define __MT_CODEC_H
#include <map>
#include <span>
#include "resiprocate/resip/stack/SdpContents.hxx" #include "resiprocate/resip/stack/SdpContents.hxx"
#include "../helper/HL_Types.h" #include "../helper/HL_Types.h"
#include <map> #include "../audio/Audio_Interface.h"
#include "../helper/HL_Pointer.h"
namespace MT namespace MT
{ {
@@ -18,12 +19,12 @@ class Codec;
typedef std::shared_ptr<Codec> PCodec; typedef std::shared_ptr<Codec> PCodec;
class CodecMap: public std::map<int, PCodec> class CodecMap: public std::map<int, PCodec>
{ {};
};
class Codec class Codec
{ {
public: public:
class Factory class Factory
{ {
public: public:
@@ -42,34 +43,51 @@ public:
resip::Codec resipCodec(); resip::Codec resipCodec();
}; };
virtual ~Codec() {} virtual ~Codec() {}
virtual const char* name() = 0;
virtual int samplerate() = 0;
virtual float timestampUnit() { return float(1.0 / samplerate()); }
// Size of decoded audio frame in bytes struct Info
virtual int pcmLength() = 0; {
std::string mName;
int mSamplerate = 0; // Hz
int mChannels = 0;
int mPcmLength = 0; // In bytes
int mFrameTime = 0; // In milliseconds
int mRtpLength = 0; // In bytes
float mTimestampUnit = 0.0f;
};
// Returns information about this codec instance
virtual Info info() = 0;
// Time length of single audio frame // Helper functions to return information - they are based on info() method
virtual int frameTime() = 0; int pcmLength() { return info().mPcmLength; }
int rtpLength() { return info().mRtpLength; }
// Size of RTP frame in bytes. Can be zero for variable sized codecs. int channels() { return info().mChannels; }
virtual int rtpLength() = 0; int samplerate() { return info().mSamplerate; }
int frameTime() { return info().mFrameTime; }
// Number of audio channels std::string name() { return info().mName; }
virtual int channels() { return 1; } float timestampUnit() { return info().mTimestampUnit == 0.0f ? 1.0f / info().mSamplerate : info().mTimestampUnit; }
Audio::Format getAudioFormat() {
return Audio::Format(this->info().mSamplerate, this->info().mChannels);
}
// Returns size of encoded data (RTP) in bytes // Returns size of encoded data (RTP) in bytes
virtual int encode(const void* input, int inputBytes, void* output, int outputCapacity) = 0; struct EncodeResult
{
size_t mEncoded = 0; // Number of encoded bytes
};
virtual EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) = 0;
// Returns size of decoded data (PCM signed short) in bytes // Returns size of decoded data (PCM signed short) in bytes
virtual int decode(const void* input, int inputBytes, void* output, int outputCapacity) = 0; struct DecodeResult
{
size_t mDecoded = 0; // Number of decoded bytes
bool mIsCng = false; // Should this packet to be used as CNG ? (used for AMR codecs)
};
virtual DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) = 0;
// Returns size of produced data (PCM signed short) in bytes // Returns size of produced data (PCM signed short) in bytes
virtual int plc(int lostFrames, void* output, int outputCapacity) = 0; virtual size_t plc(int lostFrames, std::span<uint8_t> output) = 0;
// Returns size of codec in memory
virtual int getSize() const { return 0; };
}; };
} }
#endif #endif

View File

@@ -65,6 +65,8 @@ bool CodecList::Settings::contains(int ptype) const
if (mGsmEfrPayloadType == ptype || mGsmFrPayloadType == ptype || mGsmHrPayloadType == ptype) if (mGsmEfrPayloadType == ptype || mGsmFrPayloadType == ptype || mGsmHrPayloadType == ptype)
return true; return true;
if (mTelephoneEvent == ptype)
return true;
return false; return false;
} }
@@ -122,6 +124,9 @@ std::string CodecList::Settings::toString() const
if (mGsmEfrPayloadType != -1) if (mGsmEfrPayloadType != -1)
oss << "GSM EFR ptype: " << mGsmEfrPayloadType << " "; oss << "GSM EFR ptype: " << mGsmEfrPayloadType << " ";
if (mTelephoneEvent != -1)
oss << "RFC2833 DTMF ptype: " << mTelephoneEvent;
for (auto& spec: mEvsSpec) for (auto& spec: mEvsSpec)
{ {
oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << " "; oss << "EVS ptype: " << spec.mPayloadType << ", bw: " << spec.mBandwidth << ", enc: " << (spec.mEncodingType == EvsSpec::Encoding_MIME ? "mime" : "g192") << " ";
@@ -132,6 +137,7 @@ std::string CodecList::Settings::toString() const
oss << "OPUS ptype: " << spec.mPayloadType << ", rate: " << spec.mRate << ", channels: " << spec.mChannels << std::endl; oss << "OPUS ptype: " << spec.mPayloadType << ", rate: " << spec.mRate << ", channels: " << spec.mChannels << std::endl;
} }
return oss.str(); return oss.str();
} }
@@ -151,6 +157,7 @@ void CodecList::Settings::clear()
mGsmEfrPayloadType = -1; mGsmEfrPayloadType = -1;
mGsmFrPayloadType = -1; mGsmFrPayloadType = -1;
mGsmHrPayloadType = -1; mGsmHrPayloadType = -1;
mTelephoneEvent = -1;
} }
bool CodecList::Settings::EvsSpec::isValid() const bool CodecList::Settings::EvsSpec::isValid() const
@@ -268,15 +275,16 @@ CodecList::Settings CodecList::Settings::parseSdp(const std::list<resip::Codec>&
} }
} else if (codec_name == "EVS") { } else if (codec_name == "EVS") {
r.mEvsSpec.push_back({ptype}); r.mEvsSpec.push_back({ptype});
} } else if (codec_name == "TELEPHONE-EVENT")
r.mTelephoneEvent = ptype;
} }
return r; return r;
} }
bool CodecList::Settings::operator == (const Settings& rhs) const bool CodecList::Settings::operator == (const Settings& rhs) const
{ {
if (std::tie(mWrapIuUP, mSkipDecode, mIsac16KPayloadType, mIsac32KPayloadType, mIlbc20PayloadType, mIlbc30PayloadType, mGsmFrPayloadType, mGsmFrPayloadLength, mGsmEfrPayloadType, mGsmHrPayloadType) != if (std::tie(mWrapIuUP, mSkipDecode, mIsac16KPayloadType, mIsac32KPayloadType, mIlbc20PayloadType, mIlbc30PayloadType, mGsmFrPayloadType, mGsmFrPayloadLength, mGsmEfrPayloadType, mGsmHrPayloadType, mTelephoneEvent) !=
std::tie(rhs.mWrapIuUP, rhs.mSkipDecode, rhs.mIsac16KPayloadType, rhs.mIsac32KPayloadType, rhs.mIlbc20PayloadType, rhs.mIlbc30PayloadType, rhs.mGsmFrPayloadType, rhs.mGsmFrPayloadLength, rhs.mGsmEfrPayloadType, rhs.mGsmHrPayloadType)) std::tie(rhs.mWrapIuUP, rhs.mSkipDecode, rhs.mIsac16KPayloadType, rhs.mIsac32KPayloadType, rhs.mIlbc20PayloadType, rhs.mIlbc30PayloadType, rhs.mGsmFrPayloadType, rhs.mGsmFrPayloadLength, rhs.mGsmEfrPayloadType, rhs.mGsmHrPayloadType, rhs.mTelephoneEvent))
return false; return false;
if (mAmrNbOctetPayloadType != rhs.mAmrNbOctetPayloadType) if (mAmrNbOctetPayloadType != rhs.mAmrNbOctetPayloadType)
@@ -306,6 +314,9 @@ bool CodecList::Settings::operator == (const Settings& rhs) const
if (mOpusSpec[i] != rhs.mOpusSpec[i]) if (mOpusSpec[i] != rhs.mOpusSpec[i])
return false; return false;
if (mTelephoneEvent != rhs.mTelephoneEvent)
return false;
return true; return true;
} }

View File

@@ -28,6 +28,9 @@ public:
bool mWrapIuUP = false; bool mWrapIuUP = false;
bool mSkipDecode = false; bool mSkipDecode = false;
// RFC2833 DTMF
int mTelephoneEvent = -1;
// AMR payload types // AMR payload types
std::set<int64_t> mAmrWbPayloadType = { }; std::set<int64_t> mAmrWbPayloadType = { };
std::set<int64_t> mAmrNbPayloadType = { }; std::set<int64_t> mAmrNbPayloadType = { };

View File

@@ -16,38 +16,68 @@
using namespace MT; using namespace MT;
void DtmfBuilder::buildRfc2833(int tone, int duration, int volume, bool endOfEvent, void* output) void DtmfBuilder::buildRfc2833(const Rfc2833Event& ev, void* output)
{ {
assert(duration); assert(ev.mDuration != 0);
assert(output); assert(output);
assert(tone); assert(ev.mTone != 0);
unsigned char toneValue = 0; unsigned char toneValue = 0;
if (tone >= '0' && tone <='9') if (ev.mTone >= '0' && ev.mTone <='9')
toneValue = tone - '0'; toneValue = ev.mTone - '0';
else else
if (tone >= 'A' && tone <='D' ) if (ev.mTone >= 'A' && ev.mTone <='D' )
toneValue = tone - 'A' + 12; toneValue = ev.mTone - 'A' + 12;
else else
if (tone == '*') if (ev.mTone == '*')
toneValue = 10; toneValue = 10;
else else
if (tone == '#') if (ev.mTone == '#')
toneValue = 11; toneValue = 11;
char* packet = (char*)output; char* packet = (char*)output;
packet[0] = toneValue; packet[0] = toneValue;
packet[1] = 1 | (volume << 2); packet[1] = 1 | (ev.mVolume << 2);
if (endOfEvent) if (ev.mEnd)
packet[1] |= 128; packet[1] |= 128;
else else
packet[1] &= 127; packet[1] &= 127;
unsigned short durationValue = htons(duration); unsigned short durationValue = htons(ev.mDuration);
memcpy(packet + 2, &durationValue, 2); memcpy(packet + 2, &durationValue, 2);
} }
DtmfBuilder::Rfc2833Event DtmfBuilder::parseRfc2833(std::span<uint8_t> payload)
{
Rfc2833Event r;
if (payload.size_bytes() < 4)
return r;
uint8_t b0 = payload[0];
uint8_t b1 = payload[1];
if (b0 >=0 && b0 <= 9)
r.mTone = '0' + b0;
else
if (b0 >= 12 && b0 <= 17)
r.mTone = 'A' + b0;
else
if (b0 == 10)
r.mTone = '*';
else
if (b0 == 11)
r.mTone = '#';
r.mEnd = (b1 & 128);
r.mVolume = (b1 & 127) >> 2;
r.mDuration = ntohs(*(uint16_t*)payload.data()+2);
return r;
}
#pragma region Inband DTMF support #pragma region Inband DTMF support
#include <math.h> #include <math.h>
#ifndef TARGET_WIN #ifndef TARGET_WIN
@@ -62,7 +92,7 @@ static bool sineTabInit = false;
static double sinetab[1 << 11]; static double sinetab[1 << 11];
static inline double sine(unsigned int ptr) static inline double sine(unsigned int ptr)
{ {
return sinetab[ptr >> (32-11)]; return sinetab[ptr >> (32-11)];
} }
#define TWOPI (2.0 * 3.14159265358979323846) #define TWOPI (2.0 * 3.14159265358979323846)
@@ -75,13 +105,13 @@ static inline double sine(unsigned int ptr)
static double amptab[2] = { 8191.75, 16383.5 }; static double amptab[2] = { 8191.75, 16383.5 };
static inline int ifix(double x) static inline int ifix(double x)
{ {
return (x >= 0.0) ? (int) (x+0.5) : (int) (x-0.5); return (x >= 0.0) ? (int) (x+0.5) : (int) (x-0.5);
} }
// given frequency f, return corresponding phase increment // given frequency f, return corresponding phase increment
static inline int phinc(double f) static inline int phinc(double f)
{ {
return ifix(TWO32 * f / (double) AUDIO_SAMPLERATE); return ifix(TWO32 * f / (double) AUDIO_SAMPLERATE);
} }
static char dtmfSymbols[16] = { static char dtmfSymbols[16] = {
@@ -106,31 +136,31 @@ static char dtmfSymbols[16] = {
char PDTMFEncoder_DtmfChar(int i) char PDTMFEncoder_DtmfChar(int i)
{ {
if (i < 16) if (i < 16)
return dtmfSymbols[i]; return dtmfSymbols[i];
else else
return 0; return 0;
} }
// DTMF frequencies as per http://www.commlinx.com.au/DTMF_frequencies.htm // DTMF frequencies as per http://www.commlinx.com.au/DTMF_frequencies.htm
static double dtmfFreqs[16][2] = { static double dtmfFreqs[16][2] = {
{ 941.0, 1336.0 }, // 0 { 941.0, 1336.0 }, // 0
{ 697.0, 1209.0 }, // 1 { 697.0, 1209.0 }, // 1
{ 697.0, 1336.0 }, // 2 { 697.0, 1336.0 }, // 2
{ 697.0, 1477.0 }, // 3 { 697.0, 1477.0 }, // 3
{ 770.0, 1209.0 }, // 4 { 770.0, 1209.0 }, // 4
{ 770.0, 1336.0 }, // 5 { 770.0, 1336.0 }, // 5
{ 770.0, 1477.0 }, // 6 { 770.0, 1477.0 }, // 6
{ 852.0, 1209.0 }, // 7 { 852.0, 1209.0 }, // 7
{ 852.0, 1336.0 }, // 8 { 852.0, 1336.0 }, // 8
{ 852.0, 1477.0 }, // 9 { 852.0, 1477.0 }, // 9
{ 697.0, 1633.0 }, // A { 697.0, 1633.0 }, // A
{ 770.0, 1633.0 }, // B { 770.0, 1633.0 }, // B
{ 852.0, 1633.0 }, // C { 852.0, 1633.0 }, // C
{ 941.0, 1633.0 }, // D { 941.0, 1633.0 }, // D
{ 941.0, 1209.0 }, // * { 941.0, 1209.0 }, // *
{ 941.0, 1477.0 } // # { 941.0, 1477.0 } // #
}; };
@@ -138,81 +168,81 @@ static Mutex LocalDtmfMutex;
void PDTMFEncoder_MakeSineTable() void PDTMFEncoder_MakeSineTable()
{ {
Lock lock(LocalDtmfMutex); Lock lock(LocalDtmfMutex);
if (!sineTabInit) { if (!sineTabInit) {
for (int k = 0; k < SINELEN; k++) { for (int k = 0; k < SINELEN; k++) {
double th = TWOPI * (double) k / (double) SINELEN; double th = TWOPI * (double) k / (double) SINELEN;
double v = sin(th); double v = sin(th);
sinetab[k] = v; sinetab[k] = v;
}
sineTabInit = true;
} }
sineTabInit = true;
}
} }
void PDTMFEncoder_AddTone(double f1, double f2, unsigned ms1, unsigned ms2, unsigned rate, short* result) void PDTMFEncoder_AddTone(double f1, double f2, unsigned ms1, unsigned ms2, unsigned rate, short* result)
{ {
int ak = 0; int ak = 0;
PDTMFEncoder_MakeSineTable(); PDTMFEncoder_MakeSineTable();
int dataPtr = 0; int dataPtr = 0;
double amp = amptab[ak]; double amp = amptab[ak];
int phinc1 = phinc(f1), phinc2 = phinc(f2); int phinc1 = phinc(f1), phinc2 = phinc(f2);
int ns1 = ms1 * (rate/1000); int ns1 = ms1 * (rate/1000);
int ns2 = ms2 * (rate/1000); int ns2 = ms2 * (rate/1000);
unsigned int ptr1 = 0, ptr2 = 0; unsigned int ptr1 = 0, ptr2 = 0;
ptr1 += phinc1 * ns1; ptr1 += phinc1 * ns1;
ptr2 += phinc2 * ns1; ptr2 += phinc2 * ns1;
for (int n = ns1; n < ns2; n++) { for (int n = ns1; n < ns2; n++) {
double val = amp * (sine(ptr1) + sine(ptr2)); double val = amp * (sine(ptr1) + sine(ptr2));
int ival = ifix(val); int ival = ifix(val);
if (ival < -32768) if (ival < -32768)
ival = -32768; ival = -32768;
else if (val > 32767) else if (val > 32767)
ival = 32767; ival = 32767;
result[dataPtr++] = ival / 2; result[dataPtr++] = ival / 2;
ptr1 += phinc1; ptr1 += phinc1;
ptr2 += phinc2; ptr2 += phinc2;
} }
} }
void PDTMFEncoder_AddTone(char _digit, unsigned startTime, unsigned finishTime, unsigned rate, short* result) void PDTMFEncoder_AddTone(char _digit, unsigned startTime, unsigned finishTime, unsigned rate, short* result)
{ {
char digit = (char)toupper(_digit); char digit = (char)toupper(_digit);
if ('0' <= digit && digit <= '9') if ('0' <= digit && digit <= '9')
digit = digit - '0'; digit = digit - '0';
else if ('A' <= digit && digit <= 'D') else if ('A' <= digit && digit <= 'D')
digit = digit + 10 - 'A'; digit = digit + 10 - 'A';
else if (digit == '*') else if (digit == '*')
digit = 14; digit = 14;
else if (digit == '#') else if (digit == '#')
digit = 15; digit = 15;
else else
return ; return ;
PDTMFEncoder_AddTone(dtmfFreqs[(int)digit][0], dtmfFreqs[(int)digit][1], startTime, finishTime, rate, result); PDTMFEncoder_AddTone(dtmfFreqs[(int)digit][0], dtmfFreqs[(int)digit][1], startTime, finishTime, rate, result);
} }
#pragma endregion #pragma endregion
void DtmfBuilder::buildInband(int tone, int startTime, int finishTime, int rate, short* buf) void DtmfBuilder::buildInband(int tone, int startTime, int finishTime, int rate, short* buf)
{ {
PDTMFEncoder_AddTone(tone, startTime, finishTime, rate, buf); PDTMFEncoder_AddTone(tone, startTime, finishTime, rate, buf);
} }
#pragma region DtmfContext #pragma region DtmfContext
DtmfContext::DtmfContext() DtmfContext::DtmfContext()
:mType(Dtmf_Rfc2833) :mType(Dtmf_Rfc2833)
{ {
} }
@@ -222,112 +252,112 @@ DtmfContext::~DtmfContext()
void DtmfContext::setType(Type t) void DtmfContext::setType(Type t)
{ {
mType = t; mType = t;
} }
DtmfContext::Type DtmfContext::type() DtmfContext::Type DtmfContext::type()
{ {
return mType; return mType;
} }
void DtmfContext::startTone(int tone, int volume) void DtmfContext::startTone(int tone, int volume)
{ {
Lock l(mGuard); Lock l(mGuard);
// Stop current tone if needed // Stop current tone if needed
if (mQueue.size()) if (mQueue.size())
stopTone(); stopTone();
mQueue.push_back(Dtmf(tone, volume, 0)); mQueue.push_back(Dtmf(tone, volume, 0));
} }
void DtmfContext::stopTone() void DtmfContext::stopTone()
{ {
Lock l(mGuard); Lock l(mGuard);
// Switch to "emit 3 terminating packets" mode // Switch to "emit 3 terminating packets" mode
if (mQueue.size()) if (mQueue.size())
{
switch (mType)
{ {
case Dtmf_Rfc2833: switch (mType)
mQueue.front().mStopped = true; {
mQueue.erase(mQueue.begin()); case Dtmf_Rfc2833:
break; mQueue.front().mStopped = true;
mQueue.erase(mQueue.begin());
break;
case Dtmf_Inband: case Dtmf_Inband:
if (!mQueue.front().mFinishCount) if (!mQueue.front().mFinishCount)
mQueue.front().mFinishCount = MT_DTMF_END_PACKETS; mQueue.front().mFinishCount = MT_DTMF_END_PACKETS;
break; break;
}
} }
}
} }
void DtmfContext::queueTone(int tone, int volume, int duration) void DtmfContext::queueTone(int tone, int volume, int duration)
{ {
Lock l(mGuard); Lock l(mGuard);
mQueue.push_back(Dtmf(tone, volume, duration)); mQueue.push_back(Dtmf(tone, volume, duration));
} }
void DtmfContext::clearAllTones() void DtmfContext::clearAllTones()
{ {
Lock l(mGuard); Lock l(mGuard);
mQueue.clear(); mQueue.clear();
} }
bool DtmfContext::getInband(int milliseconds, int rate, ByteBuffer& output) bool DtmfContext::getInband(int milliseconds, int rate, ByteBuffer& output)
{ {
Lock l(mGuard); Lock l(mGuard);
if (!mQueue.size() || mType != Dtmf_Inband) if (!mQueue.size() || mType != Dtmf_Inband)
return false; return false;
// //
Dtmf& d = mQueue.front(); Dtmf& d = mQueue.front();
output.resize(milliseconds * rate / 1000 * 2); output.resize((uint64_t)milliseconds * rate / 1000 * 2);
DtmfBuilder::buildInband(d.mTone, d.mCurrentTime, d.mCurrentTime + milliseconds, rate, (short*)output.mutableData()); DtmfBuilder::buildInband(d.mTone, d.mCurrentTime, d.mCurrentTime + milliseconds, rate, (short*)output.mutableData());
d.mCurrentTime += milliseconds; d.mCurrentTime += milliseconds;
return true; return true;
} }
bool DtmfContext::getRfc2833(int milliseconds, ByteBuffer& output, ByteBuffer& stopPacket) bool DtmfContext::getRfc2833(int milliseconds, ByteBuffer& output, ByteBuffer& stopPacket)
{ {
Lock l(mGuard); Lock l(mGuard);
if (!mQueue.size() || mType != Dtmf_Rfc2833) if (!mQueue.size() || mType != Dtmf_Rfc2833)
return false; return false;
Dtmf& d = mQueue.front(); Dtmf& d = mQueue.front();
// See if tone has enough duration to produce another packet // See if tone has enough duration to produce another packet
if (d.mDuration > 0) if (d.mDuration > 0)
{ {
// Emit rfc2833 packet // Emit rfc2833 packet
output.resize(4); output.resize(4);
DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, false, output.mutableData()); DtmfBuilder::buildRfc2833({.mTone = (char)d.mTone, .mDuration = milliseconds, .mVolume = d.mVolume, .mEnd = false}, output.mutableData());
d.mDuration -= milliseconds; d.mDuration -= milliseconds;
if(d.mDuration <= 0) if(d.mDuration <= 0)
d.mStopped = true; d.mStopped = true;
} }
else else
if (!d.mStopped) if (!d.mStopped)
{ {
output.resize(4); output.resize(4);
DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, false, output.mutableData()); DtmfBuilder::buildRfc2833({.mTone = (char)d.mTone, .mDuration = milliseconds, .mVolume = d.mVolume, .mEnd = false}, output.mutableData());
} }
else else
output.clear(); output.clear();
if (d.mStopped) if (d.mStopped)
{ {
stopPacket.resize(4); stopPacket.resize(4);
DtmfBuilder::buildRfc2833(d.mTone, milliseconds, d.mVolume, true, stopPacket.mutableData()); DtmfBuilder::buildRfc2833({.mTone = (char)d.mTone, .mDuration = milliseconds, .mVolume = d.mVolume, .mEnd = true}, stopPacket.mutableData());
} }
else else
stopPacket.clear(); stopPacket.clear();
if (d.mStopped) if (d.mStopped)
mQueue.erase(mQueue.begin()); mQueue.erase(mQueue.begin());
return true; return true;
} }
typedef struct typedef struct
@@ -375,32 +405,32 @@ void zap_dtmf_detect_init(dtmf_detect_state_t *s);
int zap_dtmf_detect(dtmf_detect_state_t *s, int16_t amp[], int samples, int isradio); int zap_dtmf_detect(dtmf_detect_state_t *s, int16_t amp[], int samples, int isradio);
int zap_dtmf_get(dtmf_detect_state_t *s, char *buf, int max); int zap_dtmf_get(dtmf_detect_state_t *s, char *buf, int max);
DTMFDetector::DTMFDetector() InbandDtmfDetector::InbandDtmfDetector()
:mState(NULL) :mState(NULL)
{ {
mState = malloc(sizeof(dtmf_detect_state_t)); mState = malloc(sizeof(dtmf_detect_state_t));
memset(mState, 0, sizeof(dtmf_detect_state_t)); memset(mState, 0, sizeof(dtmf_detect_state_t));
zap_dtmf_detect_init((dtmf_detect_state_t*)mState); zap_dtmf_detect_init((dtmf_detect_state_t*)mState);
} }
DTMFDetector::~DTMFDetector() InbandDtmfDetector::~InbandDtmfDetector()
{ {
if (mState) if (mState)
free(mState); free(mState);
} }
std::string DTMFDetector::streamPut(unsigned char* samples, unsigned int size) std::string InbandDtmfDetector::streamPut(unsigned char* samples, unsigned int size)
{ {
char buf[16]; buf[0] = 0; char buf[16]; buf[0] = 0;
if (zap_dtmf_detect((dtmf_detect_state_t*)mState, (int16_t*)samples, size/2, 0)) if (zap_dtmf_detect((dtmf_detect_state_t*)mState, (int16_t*)samples, size/2, 0))
zap_dtmf_get((dtmf_detect_state_t*)mState, buf, 15); zap_dtmf_get((dtmf_detect_state_t*)mState, buf, 15);
return buf; return buf;
} }
void DTMFDetector::resetState() void InbandDtmfDetector::resetState()
{ {
zap_dtmf_detect_init((dtmf_detect_state_t*)mState); zap_dtmf_detect_init((dtmf_detect_state_t*)mState);
} }
#ifndef TRUE #ifndef TRUE
@@ -448,12 +478,12 @@ static tone_detection_descriptor_t fax_detect;
static tone_detection_descriptor_t fax_detect_2nd; static tone_detection_descriptor_t fax_detect_2nd;
static float dtmf_row[] = static float dtmf_row[] =
{ {
697.0, 770.0, 852.0, 941.0 697.0, 770.0, 852.0, 941.0
}; };
static float dtmf_col[] = static float dtmf_col[] =
{ {
1209.0, 1336.0, 1477.0, 1633.0 1209.0, 1336.0, 1477.0, 1633.0
}; };
static float fax_freq = 1100.0; static float fax_freq = 1100.0;
@@ -461,10 +491,10 @@ static float fax_freq = 1100.0;
static char dtmf_positions[] = "123A" "456B" "789C" "*0#D"; static char dtmf_positions[] = "123A" "456B" "789C" "*0#D";
static void goertzel_init(goertzel_state_t *s, static void goertzel_init(goertzel_state_t *s,
tone_detection_descriptor_t *t) tone_detection_descriptor_t *t)
{ {
s->v2 = s->v2 =
s->v3 = 0.0; s->v3 = 0.0;
s->fac = t->fac; s->fac = t->fac;
} }
/*- End of function --------------------------------------------------------*/ /*- End of function --------------------------------------------------------*/
@@ -497,50 +527,50 @@ static inline void _dtmf_goertzel_update(goertzel_state_t *s,
//s->v3 = s->fac*s->v2 - v1 + x[0]; //s->v3 = s->fac*s->v2 - v1 + x[0];
__asm__ __volatile__ ( __asm__ __volatile__ (
" femms;\n" " femms;\n"
" movq 16(%%edx),%%mm2;\n" " movq 16(%%edx),%%mm2;\n"
" movq 24(%%edx),%%mm3;\n" " movq 24(%%edx),%%mm3;\n"
" movq 32(%%edx),%%mm4;\n" " movq 32(%%edx),%%mm4;\n"
" movq 40(%%edx),%%mm5;\n" " movq 40(%%edx),%%mm5;\n"
" movq 48(%%edx),%%mm6;\n" " movq 48(%%edx),%%mm6;\n"
" movq 56(%%edx),%%mm7;\n" " movq 56(%%edx),%%mm7;\n"
" jmp 1f;\n" " jmp 1f;\n"
" .align 32;\n" " .align 32;\n"
" 1: ;\n" " 1: ;\n"
" prefetch (%%eax);\n" " prefetch (%%eax);\n"
" movq %%mm3,%%mm1;\n" " movq %%mm3,%%mm1;\n"
" movq %%mm2,%%mm0;\n" " movq %%mm2,%%mm0;\n"
" movq %%mm5,%%mm3;\n" " movq %%mm5,%%mm3;\n"
" movq %%mm4,%%mm2;\n" " movq %%mm4,%%mm2;\n"
" pfmul %%mm7,%%mm5;\n" " pfmul %%mm7,%%mm5;\n"
" pfmul %%mm6,%%mm4;\n" " pfmul %%mm6,%%mm4;\n"
" pfsub %%mm1,%%mm5;\n" " pfsub %%mm1,%%mm5;\n"
" pfsub %%mm0,%%mm4;\n" " pfsub %%mm0,%%mm4;\n"
" movq (%%eax),%%mm0;\n" " movq (%%eax),%%mm0;\n"
" movq %%mm0,%%mm1;\n" " movq %%mm0,%%mm1;\n"
" punpckldq %%mm0,%%mm1;\n" " punpckldq %%mm0,%%mm1;\n"
" add $4,%%eax;\n" " add $4,%%eax;\n"
" pfadd %%mm1,%%mm5;\n" " pfadd %%mm1,%%mm5;\n"
" pfadd %%mm1,%%mm4;\n" " pfadd %%mm1,%%mm4;\n"
" dec %%ecx;\n" " dec %%ecx;\n"
" jnz 1b;\n" " jnz 1b;\n"
" movq %%mm2,16(%%edx);\n" " movq %%mm2,16(%%edx);\n"
" movq %%mm3,24(%%edx);\n" " movq %%mm3,24(%%edx);\n"
" movq %%mm4,32(%%edx);\n" " movq %%mm4,32(%%edx);\n"
" movq %%mm5,40(%%edx);\n" " movq %%mm5,40(%%edx);\n"
" femms;\n" " femms;\n"
: :
: "c" (samples), "a" (x), "d" (vv) : "c" (samples), "a" (x), "d" (vv)
: "memory", "eax", "ecx"); : "memory", "eax", "ecx");
s[0].v2 = vv[4]; s[0].v2 = vv[4];
s[1].v2 = vv[5]; s[1].v2 = vv[5];
@@ -555,8 +585,8 @@ static inline void _dtmf_goertzel_update(goertzel_state_t *s,
/*- End of function --------------------------------------------------------*/ /*- End of function --------------------------------------------------------*/
void zap_goertzel_update(goertzel_state_t *s, void zap_goertzel_update(goertzel_state_t *s,
int16_t x[], int16_t x[],
int samples) int samples)
{ {
int i; int i;
float v1; float v1;
@@ -582,7 +612,7 @@ void zap_dtmf_detect_init (dtmf_detect_state_t *s)
float theta; float theta;
s->hit1 = s->hit1 =
s->hit2 = 0; s->hit2 = 0;
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
{ {
@@ -598,12 +628,12 @@ void zap_dtmf_detect_init (dtmf_detect_state_t *s)
theta = float(2.0*M_PI*(dtmf_col[i]*2.0/SAMPLE_RATE)); theta = float(2.0*M_PI*(dtmf_col[i]*2.0/SAMPLE_RATE));
dtmf_detect_col_2nd[i].fac = float(2.0*cos(theta)); dtmf_detect_col_2nd[i].fac = float(2.0*cos(theta));
goertzel_init (&s->row_out[i], &dtmf_detect_row[i]); goertzel_init (&s->row_out[i], &dtmf_detect_row[i]);
goertzel_init (&s->col_out[i], &dtmf_detect_col[i]); goertzel_init (&s->col_out[i], &dtmf_detect_col[i]);
goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]); goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]);
goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]); goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]);
s->energy = 0.0; s->energy = 0.0;
} }
/* Same for the fax dector */ /* Same for the fax dector */
@@ -625,9 +655,9 @@ s->energy = 0.0;
/*- End of function --------------------------------------------------------*/ /*- End of function --------------------------------------------------------*/
int zap_dtmf_detect (dtmf_detect_state_t *s, int zap_dtmf_detect (dtmf_detect_state_t *s,
int16_t amp[], int16_t amp[],
int samples, int samples,
int isradio) int isradio)
{ {
float row_energy[4]; float row_energy[4];
@@ -666,7 +696,7 @@ int zap_dtmf_detect (dtmf_detect_state_t *s,
{ {
famp = amp[j]; famp = amp[j];
s->energy += famp*famp; s->energy += famp*famp;
/* With GCC 2.95, the following unrolled code seems to take about 35% /* With GCC 2.95, the following unrolled code seems to take about 35%
(rough estimate) as long as a neat little 0-3 loop */ (rough estimate) as long as a neat little 0-3 loop */
@@ -734,7 +764,7 @@ int zap_dtmf_detect (dtmf_detect_state_t *s,
s->row_out2nd[3].v2 = s->row_out2nd[3].v3; s->row_out2nd[3].v2 = s->row_out2nd[3].v3;
s->row_out2nd[3].v3 = s->row_out2nd[3].fac*s->row_out2nd[3].v2 - v1 + famp; s->row_out2nd[3].v3 = s->row_out2nd[3].fac*s->row_out2nd[3].v2 - v1 + famp;
/* Update fax tone */ /* Update fax tone */
v1 = s->fax_tone.v2; v1 = s->fax_tone.v2;
s->fax_tone.v2 = s->fax_tone.v3; s->fax_tone.v2 = s->fax_tone.v3;
s->fax_tone.v3 = s->fax_tone.fac*s->fax_tone.v2 - v1 + famp; s->fax_tone.v3 = s->fax_tone.fac*s->fax_tone.v2 - v1 + famp;
@@ -748,28 +778,28 @@ int zap_dtmf_detect (dtmf_detect_state_t *s,
if (s->current_sample < 102) if (s->current_sample < 102)
continue; continue;
/* Detect the fax energy, too */ /* Detect the fax energy, too */
fax_energy = zap_goertzel_result(&s->fax_tone); fax_energy = zap_goertzel_result(&s->fax_tone);
/* We are at the end of a DTMF detection block */ /* We are at the end of a DTMF detection block */
/* Find the peak row and the peak column */ /* Find the peak row and the peak column */
row_energy[0] = zap_goertzel_result (&s->row_out[0]); row_energy[0] = zap_goertzel_result (&s->row_out[0]);
col_energy[0] = zap_goertzel_result (&s->col_out[0]); col_energy[0] = zap_goertzel_result (&s->col_out[0]);
for (best_row = best_col = 0, i = 1; i < 4; i++) for (best_row = best_col = 0, i = 1; i < 4; i++)
{ {
row_energy[i] = zap_goertzel_result (&s->row_out[i]); row_energy[i] = zap_goertzel_result (&s->row_out[i]);
if (row_energy[i] > row_energy[best_row]) if (row_energy[i] > row_energy[best_row])
best_row = i; best_row = i;
col_energy[i] = zap_goertzel_result (&s->col_out[i]); col_energy[i] = zap_goertzel_result (&s->col_out[i]);
if (col_energy[i] > col_energy[best_col]) if (col_energy[i] > col_energy[best_col])
best_col = i; best_col = i;
} }
hit = 0; hit = 0;
/* Basic signal level test and the twist test */ /* Basic signal level test and the twist test */
if (row_energy[best_row] >= DTMF_THRESHOLD if (row_energy[best_row] >= DTMF_THRESHOLD
&& &&
col_energy[best_col] >= DTMF_THRESHOLD col_energy[best_col] >= DTMF_THRESHOLD
&& &&
col_energy[best_col] < row_energy[best_row]*DTMF_REVERSE_TWIST col_energy[best_col] < row_energy[best_row]*DTMF_REVERSE_TWIST
&& &&
@@ -787,8 +817,8 @@ for (best_row = best_col = 0, i = 1; i < 4; i++)
} }
/* ... and second harmonic test */ /* ... and second harmonic test */
if (i >= 4 if (i >= 4
&& &&
(row_energy[best_row] + col_energy[best_col]) > 42.0*s->energy (row_energy[best_row] + col_energy[best_col]) > 42.0*s->energy
&& &&
zap_goertzel_result (&s->col_out2nd[best_col])*DTMF_2ND_HARMONIC_COL < col_energy[best_col] zap_goertzel_result (&s->col_out2nd[best_col])*DTMF_2ND_HARMONIC_COL < col_energy[best_col]
&& &&
@@ -804,7 +834,7 @@ for (best_row = best_col = 0, i = 1; i < 4; i++)
to a digit. */ to a digit. */
if (hit == s->hit3 && s->hit3 != s->hit2) if (hit == s->hit3 && s->hit3 != s->hit2)
{ {
s->mhit = hit; s->mhit = hit;
s->digit_hits[(best_row << 2) + best_col]++; s->digit_hits[(best_row << 2) + best_col]++;
s->detected_digits++; s->detected_digits++;
if (s->current_digits < MAX_DTMF_DIGITS) if (s->current_digits < MAX_DTMF_DIGITS)
@@ -819,60 +849,60 @@ for (best_row = best_col = 0, i = 1; i < 4; i++)
} }
} }
} }
if (!hit && (fax_energy >= FAX_THRESHOLD) && (fax_energy > s->energy * 21.0)) { if (!hit && (fax_energy >= FAX_THRESHOLD) && (fax_energy > s->energy * 21.0)) {
fax_energy_2nd = zap_goertzel_result(&s->fax_tone2nd); fax_energy_2nd = zap_goertzel_result(&s->fax_tone2nd);
if (fax_energy_2nd * FAX_2ND_HARMONIC < fax_energy) { if (fax_energy_2nd * FAX_2ND_HARMONIC < fax_energy) {
#if 0 #if 0
printf("Fax energy/Second Harmonic: %f/%f\n", fax_energy, fax_energy_2nd); printf("Fax energy/Second Harmonic: %f/%f\n", fax_energy, fax_energy_2nd);
#endif #endif
/* XXX Probably need better checking than just this the energy XXX */ /* XXX Probably need better checking than just this the energy XXX */
hit = 'f'; hit = 'f';
s->fax_hits++; s->fax_hits++;
} /* Don't reset fax hits counter */ } /* Don't reset fax hits counter */
} else { } else {
if (s->fax_hits > 5) { if (s->fax_hits > 5) {
s->mhit = 'f'; s->mhit = 'f';
s->detected_digits++; s->detected_digits++;
if (s->current_digits < MAX_DTMF_DIGITS) if (s->current_digits < MAX_DTMF_DIGITS)
{ {
s->digits[s->current_digits++] = hit; s->digits[s->current_digits++] = hit;
s->digits[s->current_digits] = '\0'; s->digits[s->current_digits] = '\0';
} }
else else
{ {
s->lost_digits++; s->lost_digits++;
} }
} }
s->fax_hits = 0; s->fax_hits = 0;
} }
s->hit1 = s->hit2; s->hit1 = s->hit2;
s->hit2 = s->hit3; s->hit2 = s->hit3;
s->hit3 = hit; s->hit3 = hit;
/* Reinitialise the detector for the next block */ /* Reinitialise the detector for the next block */
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
{ {
goertzel_init (&s->row_out[i], &dtmf_detect_row[i]); goertzel_init (&s->row_out[i], &dtmf_detect_row[i]);
goertzel_init (&s->col_out[i], &dtmf_detect_col[i]); goertzel_init (&s->col_out[i], &dtmf_detect_col[i]);
goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]); goertzel_init (&s->row_out2nd[i], &dtmf_detect_row_2nd[i]);
goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]); goertzel_init (&s->col_out2nd[i], &dtmf_detect_col_2nd[i]);
} }
goertzel_init (&s->fax_tone, &fax_detect); goertzel_init (&s->fax_tone, &fax_detect);
goertzel_init (&s->fax_tone2nd, &fax_detect_2nd); goertzel_init (&s->fax_tone2nd, &fax_detect_2nd);
s->energy = 0.0; s->energy = 0.0;
s->current_sample = 0; s->current_sample = 0;
} }
if ((!s->mhit) || (s->mhit != hit)) if ((!s->mhit) || (s->mhit != hit))
{ {
s->mhit = 0; s->mhit = 0;
return(0); return(0);
} }
return (hit); return (hit);
} }
/*- End of function --------------------------------------------------------*/ /*- End of function --------------------------------------------------------*/
int zap_dtmf_get (dtmf_detect_state_t *s, int zap_dtmf_get (dtmf_detect_state_t *s,
char *buf, char *buf,
int max) int max)
{ {
if (max > s->current_digits) if (max > s->current_digits)
max = s->current_digits; max = s->current_digits;

View File

@@ -15,18 +15,32 @@
namespace MT namespace MT
{ {
class DtmfBuilder class DtmfBuilder
{ {
public: public:
struct Rfc2833Event
{
char mTone = 0;
int mDuration = 0;
int mVolume = 0;
bool mEnd = false;
bool isValid() const {
return mTone != 0;
}
};
// Output should be 4 bytes length // Output should be 4 bytes length
static void buildRfc2833(int tone, int duration, int volume, bool endOfEvent, void* output); static void buildRfc2833(const Rfc2833Event& ev, void* output);
static Rfc2833Event parseRfc2833(std::span<uint8_t> payload);
// Buf receives PCM audio // Buf receives PCM audio
static void buildInband(int tone, int startTime, int finishTime, int rate, short* buf); static void buildInband(int tone, int startTime, int finishTime, int rate, short* buf);
}; };
struct Dtmf struct Dtmf
{ {
Dtmf(): mTone(0), mDuration(0), mVolume(0), mFinishCount(3), mCurrentTime(0), mStopped(false) {} Dtmf(): mTone(0), mDuration(0), mVolume(0), mFinishCount(3), mCurrentTime(0), mStopped(false) {}
Dtmf(int tone, int volume, int duration): mTone(tone), mVolume(volume), mDuration(duration), mFinishCount(3), mCurrentTime(0), mStopped(false) {} Dtmf(int tone, int volume, int duration): mTone(tone), mVolume(volume), mDuration(duration), mFinishCount(3), mCurrentTime(0), mStopped(false) {}
@@ -36,17 +50,17 @@ namespace MT
int mVolume; int mVolume;
int mFinishCount; int mFinishCount;
int mCurrentTime; int mCurrentTime;
}; };
typedef std::vector<Dtmf> DtmfQueue; typedef std::vector<Dtmf> DtmfQueue;
class DtmfContext class DtmfContext
{ {
public: public:
enum Type enum Type
{ {
Dtmf_Inband, Dtmf_Inband,
Dtmf_Rfc2833 Dtmf_Rfc2833
}; };
DtmfContext(); DtmfContext();
@@ -65,33 +79,34 @@ namespace MT
bool getInband(int milliseconds, int rate, ByteBuffer& output); bool getInband(int milliseconds, int rate, ByteBuffer& output);
bool getRfc2833(int milliseconds, ByteBuffer& output, ByteBuffer& stopPacket); bool getRfc2833(int milliseconds, ByteBuffer& output, ByteBuffer& stopPacket);
protected: protected:
Mutex mGuard; Mutex mGuard;
Type mType; Type mType;
DtmfQueue mQueue; DtmfQueue mQueue;
}; };
class DTMFDetector class InbandDtmfDetector
{ {
public: public:
/*! The default constructor. Allocates space for detector context. */ /*! The default constructor. Allocates space for detector context. */
DTMFDetector(); InbandDtmfDetector();
/*! The destructor. Free the detector context's memory. */ /*! The destructor. Free the detector context's memory. */
~DTMFDetector(); ~InbandDtmfDetector();
/*! This method receives the input PCM 16-bit data and returns found DTMF event(s) in string representation. /*! This method receives the input PCM 16-bit data and returns found DTMF event(s) in string representation.
* @param samples Input PCM buffer pointer. * @param samples Input PCM buffer pointer.
* @param size Size of input buffer in bytes * @param size Size of input buffer in bytes
* @return Found DTMF event(s) in string representation. The returned value has variable length. * @return Found DTMF event(s) in string representation. The returned value has variable length.
*/ */
std::string streamPut(unsigned char* samples, unsigned int size); std::string streamPut(unsigned char* samples, unsigned int size);
void resetState(); void resetState();
protected: protected:
void* mState; /// DTMF detector context void* mState; /// DTMF detector context
}; };
} }
#endif #endif

View File

@@ -1,6 +1,6 @@
#include "MT_EvsCodec.h" #include "MT_EvsCodec.h"
#include <set>
#include <map>
/*-------------------------------------------------------------------* /*-------------------------------------------------------------------*
* rate2AMRWB_IOmode() * rate2AMRWB_IOmode()
@@ -167,67 +167,58 @@ EVSCodec::~EVSCodec()
} }
} }
int EVSCodec::samplerate() Codec::Info EVSCodec::info() {
{ return {
return st_dec->output_Fs; .mName = MT_EVS_CODECNAME,
.mSamplerate = st_dec->output_Fs,
.mChannels = 1,
.mPcmLength = st_dec->output_Fs / 1000 * sp.ptime * 2,
.mFrameTime = sp.ptime,
.mRtpLength = 0
};
} }
int EVSCodec::pcmLength()
{
return samplerate() / 50 * 2;
}
int EVSCodec::frameTime() Codec::EncodeResult EVSCodec::encode(std::span<const uint8_t> input, std::span<uint8_t> output)
{
return sp.ptime;
}
int EVSCodec::rtpLength()
{
// Variable sized codec - bitrate can be changed during the call
return 0;
}
int EVSCodec::encode(const void* input, int inputBytes, void* output, int outputCapacity)
{ {
// Encoding is not supported yet. // Encoding is not supported yet.
return 0; return {.mEncoded = 0};
} }
int EVSCodec::decode(const void* input, int input_length, void* output, int outputCapacity) Codec::DecodeResult EVSCodec::decode(std::span<const uint8_t> input, std::span<uint8_t> output)
{ {
if (outputCapacity < pcmLength()) if (output.size_bytes() < pcmLength())
return 0; return {.mDecoded = 0};
std::string buffer; std::string buffer;
// Check if we get payload with CMR // Check if we get payload with CMR
auto payload_iter = FixedPayload_EVSPrimary.find((input_length - 2) * 8); auto payload_iter = FixedPayload_EVSPrimary.find((input.size_bytes() - 2) * 8);
if (payload_iter == FixedPayload_EVSPrimary.end()) if (payload_iter == FixedPayload_EVSPrimary.end())
{ {
// Check if we get payload with ToC and without CMR // Check if we get payload with ToC and without CMR
payload_iter = FixedPayload_EVSPrimary.find((input_length - 1) * 8); payload_iter = FixedPayload_EVSPrimary.find((input.size_bytes() - 1) * 8);
if (payload_iter == FixedPayload_EVSPrimary.end()) if (payload_iter == FixedPayload_EVSPrimary.end())
{ {
// Maybe there is no ToC ? // Maybe there is no ToC ?
payload_iter = FixedPayload_EVSPrimary.find(input_length * 8); payload_iter = FixedPayload_EVSPrimary.find(input.size_bytes() * 8);
if (payload_iter == FixedPayload_EVSPrimary.end()) if (payload_iter == FixedPayload_EVSPrimary.end())
{ {
// Bad payload size at all // Bad payload size at all
return 0; return {.mDecoded = 0};
} }
/* Add ToC byte. /* Add ToC byte.
* WARNING maybe it will be work incorrect with 56bit payload, * WARNING maybe it will be work incorrect with 56bit payload,
* see 3GPP TS 26.445 Annex A, A.2.1.3 */ * see 3GPP TS 26.445 Annex A, A.2.1.3 */
char c = evs::rate2EVSmode(FixedPayload_EVSPrimary.find(input_length * 8)->second); char c = evs::rate2EVSmode(FixedPayload_EVSPrimary.find(input.size_bytes() * 8)->second);
buffer += c; buffer += c;
buffer += std::string(reinterpret_cast<const char*>(input), input_length); buffer += std::string(reinterpret_cast<const char*>(input.data()), input.size_bytes());
} }
else else
buffer = std::string(reinterpret_cast<const char*>(input), input_length); buffer = std::string(reinterpret_cast<const char*>(input.data()), input.size_bytes());
} }
else // Skip CMR byte else // Skip CMR byte
buffer = std::string(reinterpret_cast<const char*>(input) + 1, input_length-1); buffer = std::string(reinterpret_cast<const char*>(input.data()) + 1, input.size_bytes()-1);
// Output buffer for 48 KHz // Output buffer for 48 KHz
@@ -263,7 +254,7 @@ int EVSCodec::decode(const void* input, int input_length, void* output, int outp
} }
/* convert 'float' output data to 'short' */ /* convert 'float' output data to 'short' */
evs::syn_output(data, static_cast<short>(pcmLength() / 2), static_cast<short*>(output) + offset); evs::syn_output(data, static_cast<short>(pcmLength() / 2), reinterpret_cast<short*>(output.data()) + offset);
offset += pcmLength() / 2; offset += pcmLength() / 2;
if (st_dec->ini_frame < MAX_FRAME_COUNTER) if (st_dec->ini_frame < MAX_FRAME_COUNTER)
{ {
@@ -271,12 +262,12 @@ int EVSCodec::decode(const void* input, int input_length, void* output, int outp
} }
} }
return pcmLength(); return {.mDecoded = (size_t)pcmLength()};
} }
int EVSCodec::plc(int lostFrames, void* output, int outputCapacity) size_t EVSCodec::plc(int lostFrames, std::span<uint8_t> output)
{ {
return 0; return 0;
} }
void EVSCodec::initDecoder(const StreamParameters& sp) void EVSCodec::initDecoder(const StreamParameters& sp)

View File

@@ -2,15 +2,11 @@
#define __MT_EVS_CODEC_H #define __MT_EVS_CODEC_H
#include "../engine_config.h" #include "../engine_config.h"
#include <set>
#include <map>
#include <memory>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <assert.h> #include <assert.h>
#include <sstream>
#include "MT_Codec.h" #include "MT_Codec.h"
@@ -52,18 +48,14 @@ public:
EVSCodec(const StreamParameters& sp); EVSCodec(const StreamParameters& sp);
~EVSCodec() override; ~EVSCodec() override;
const char* name() override { return MT_EVS_CODECNAME; } Info info() override;
int samplerate() override;
int pcmLength() override; EncodeResult encode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int frameTime() override; DecodeResult decode(std::span<const uint8_t> input, std::span<uint8_t> output) override;
int rtpLength() override; size_t plc(int lostFrames, std::span<uint8_t> output) override;
int encode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int decode(const void* input, int inputBytes, void* output, int outputCapacity) override;
int plc(int lostFrames, void* output, int outputCapacity) override;
private: private:
evs::Decoder_State* st_dec; evs::Decoder_State* st_dec = nullptr;
//Encoder_State_fx* st_enc;
StreamParameters sp; StreamParameters sp;
void initDecoder(const StreamParameters& sp); void initDecoder(const StreamParameters& sp);
}; };

View File

@@ -13,19 +13,17 @@
using namespace MT; using namespace MT;
SingleAudioStream::SingleAudioStream(const CodecList::Settings& codecSettings, Statistics& stat) SingleAudioStream::SingleAudioStream(const CodecList::Settings& codecSettings, Statistics& stat)
:mReceiver(codecSettings, stat), mDtmfReceiver(stat) :mReceiver(codecSettings, stat),
{ mDtmfReceiver(stat)
} {}
SingleAudioStream::~SingleAudioStream() SingleAudioStream::~SingleAudioStream()
{ {}
}
void SingleAudioStream::process(const std::shared_ptr<jrtplib::RTPPacket>& packet) void SingleAudioStream::process(const std::shared_ptr<jrtplib::RTPPacket>& packet)
{ {
ICELogMedia(<< "Processing incoming RTP/RTCP packet"); ICELogMedia(<< "Processing incoming RTP/RTCP packet");
if (packet->GetPayloadType() == 101/*resip::Codec::TelephoneEvent.payloadType()*/) if (packet->GetPayloadType() == mReceiver.getCodecSettings().mTelephoneEvent)
mDtmfReceiver.add(packet); mDtmfReceiver.add(packet);
else else
mReceiver.add(packet); mReceiver.add(packet);
@@ -33,9 +31,10 @@ void SingleAudioStream::process(const std::shared_ptr<jrtplib::RTPPacket>& packe
void SingleAudioStream::copyPcmTo(Audio::DataWindow& output, int needed) void SingleAudioStream::copyPcmTo(Audio::DataWindow& output, int needed)
{ {
// Packet by packet
while (output.filled() < needed) while (output.filled() < needed)
{ {
if (mReceiver.getAudio(output, {}) != AudioReceiver::DecodeResult_Ok) if (mReceiver.getAudioTo(output, {}).mStatus != AudioReceiver::DecodeResult::Status::Ok)
break; break;
} }

View File

@@ -13,19 +13,19 @@
#include "MT_AudioReceiver.h" #include "MT_AudioReceiver.h"
namespace MT namespace MT
{ {
class SingleAudioStream class SingleAudioStream
{ {
public: public:
SingleAudioStream(const CodecList::Settings& codecSettings, Statistics& stat); SingleAudioStream(const CodecList::Settings& codecSettings, Statistics& stat);
~SingleAudioStream(); ~SingleAudioStream();
void process(const std::shared_ptr<jrtplib::RTPPacket>& packet); void process(const std::shared_ptr<jrtplib::RTPPacket>& packet);
void copyPcmTo(Audio::DataWindow& output, int needed); void copyPcmTo(Audio::DataWindow& output, int needed);
protected: protected:
DtmfReceiver mDtmfReceiver; DtmfReceiver mDtmfReceiver;
AudioReceiver mReceiver; AudioReceiver mReceiver;
}; };
typedef std::map<unsigned, SingleAudioStream*> AudioStreamMap; typedef std::map<unsigned, SingleAudioStream*> AudioStreamMap;
} }
#endif #endif

View File

@@ -212,15 +212,13 @@ void SrtpSession::close()
SrtpKeySalt& SrtpSession::outgoingKey(SrtpSuite suite) SrtpKeySalt& SrtpSession::outgoingKey(SrtpSuite suite)
{ {
Lock l(mGuard);
assert(suite > SRTP_NONE && suite <= SRTP_LAST); assert(suite > SRTP_NONE && suite <= SRTP_LAST);
return mOutgoingKey[int(suite)-1]; Lock l(mGuard);
return mOutgoingKey[int(suite)-1]; // The automated review sometimes give the hints about the possible underflow array index access
} }
bool SrtpSession::protectRtp(void* buffer, int* length) bool SrtpSession::protectRtp(void* buffer, int* length)
{ {
// addSsrc(RtpHelper::findSsrc(buffer, *length), sdOutgoing);
Lock l(mGuard); Lock l(mGuard);
if (mOutboundSession) if (mOutboundSession)
{ {

View File

@@ -20,20 +20,17 @@
enum SrtpSuite enum SrtpSuite
{ {
SRTP_NONE, SRTP_NONE = 0,
SRTP_AES_128_AUTH_80, SRTP_AES_128_AUTH_80 = 1,
SRTP_AES_256_AUTH_80, SRTP_AES_256_AUTH_80 = 2,
SRTP_AES_192_AUTH_80, SRTP_AES_192_AUTH_80 = 3,
SRTP_AES_128_AUTH_32, SRTP_AES_128_AUTH_32 = 4,
SRTP_AES_256_AUTH_32, SRTP_AES_256_AUTH_32 = 5,
SRTP_AES_192_AUTH_32, SRTP_AES_192_AUTH_32 = 6,
SRTP_AES_128_AUTH_NULL, SRTP_AES_128_AUTH_NULL = 7,
SRTP_AED_AES_256_GCM, SRTP_AED_AES_256_GCM = 8,
SRTP_AED_AES_128_GCM, SRTP_AED_AES_128_GCM = 9,
SRTP_LAST = SRTP_AED_AES_128_GCM SRTP_LAST = SRTP_AED_AES_128_GCM
// ToDo:
// a=crypto:1 AEAD_AES_256_GCM_8 inline:tN2A0vRjFBimpQsW2GasuJuPe7hKE26gki30APC8DVuySqCOYTs8lYBPR5I=
// a=crypto:3 AEAD_AES_128_GCM_8 inline:Ok7VL8SmBHSbZLw4dK6iQgpliYKGdY9BHLJcRw==
}; };
extern SrtpSuite toSrtpSuite(const std::string_view& s); extern SrtpSuite toSrtpSuite(const std::string_view& s);

View File

@@ -74,13 +74,16 @@ Statistics::~Statistics()
void Statistics::calculateBurstr(double* burstr, double* lossr) const void Statistics::calculateBurstr(double* burstr, double* lossr) const
{ {
int lost = 0; int lost = 0; // Total packet lost
int bursts = 0; for (const auto& item: mPacketLossTimeline)
for (int i = 0; i < 128; i++) lost += item.mGap;
{ int bursts = mPacketLossTimeline.size(); // number of events
lost += i * mLoss[i];
bursts += mLoss[i]; // for (const auto& entry: mLoss)
} // {
// lost += entry.first * entry.second;
// bursts += entry.second;
// }
if (lost < 5) if (lost < 5)
{ {
@@ -109,14 +112,14 @@ void Statistics::calculateBurstr(double* burstr, double* lossr) const
double Statistics::calculateMos(double maximalMos) const double Statistics::calculateMos(double maximalMos) const
{ {
// calculate lossrate and burst rate // calculate lossrate and burst rate
double burstr, lossr; double burstr = 0, lossr = 0;
calculateBurstr(&burstr, &lossr); calculateBurstr(&burstr, &lossr);
double r; double r = 0.0;
double bpl = 8.47627; //mos = -4.23836 + 0.29873 * r - 0.00416744 * r * r + 0.0000209855 * r * r * r; double bpl = 8.47627; //mos = -4.23836 + 0.29873 * r - 0.00416744 * r * r + 0.0000209855 * r * r * r;
double mos; double mos = 0.0;
if (mReceivedRtp < 100) if (mReceivedRtp < 10)
return 0.0; return 0.0;
if (lossr == 0.0 || burstr == 0.0) if (lossr == 0.0 || burstr == 0.0)

View File

@@ -4,16 +4,15 @@
#include <chrono> #include <chrono>
#include <map> #include <map>
#include <optional> #include <optional>
#include <array>
#include "audio/Audio_DataWindow.h"
#include "helper/HL_Optional.hpp"
#include "helper/HL_Statistics.h" #include "helper/HL_Statistics.h"
#include "helper/HL_Types.h" #include "helper/HL_Types.h"
#include "helper/HL_InternetAddress.h"
#include "jrtplib/src/rtptimeutilities.h" #include "jrtplib/src/rtptimeutilities.h"
#include "jrtplib/src/rtppacket.h" #include "jrtplib/src/rtppacket.h"
using namespace std::chrono_literals;
namespace MT namespace MT
{ {
@@ -56,46 +55,55 @@ struct PacketLossEvent
uint32_t mStartSeqno = 0, uint32_t mStartSeqno = 0,
mEndSeqno = 0; mEndSeqno = 0;
int mGap = 0; int mGap = 0;
std::chrono::microseconds mTimestampStart = 0us,
mTimestampEnd = 0us;
};
struct Dtmf2833Event
{
char mTone;
std::chrono::microseconds mTimestamp; std::chrono::microseconds mTimestamp;
}; };
class Statistics class Statistics
{ {
public: public:
size_t mReceived = 0, // Received traffic in bytes size_t mReceived = 0, // Received traffic in bytes
mSent = 0, // Sent traffic in bytes mSent = 0, // Sent traffic in bytes
mReceivedRtp = 0, // Number of received rtp packets mReceivedRtp = 0, // Number of received rtp packets
mSentRtp = 0, // Number of sent rtp packets mSentRtp = 0, // Number of sent rtp packets
mReceivedRtcp = 0, // Number of received rtcp packets mReceivedRtcp = 0, // Number of received rtcp packets
mSentRtcp = 0, // Number of sent rtcp packets mSentRtcp = 0, // Number of sent rtcp packets
mDuplicatedRtp = 0, // Number of received duplicated rtp packets mDuplicatedRtp = 0, // Number of received duplicated rtp packets
mOldRtp = 0, // Number of late rtp packets mOldRtp = 0, // Number of late rtp packets
mPacketLoss = 0, // Number of lost packets mPacketLoss = 0, // Number of lost packets
mPacketDropped = 0, // Number of dropped packets (due to time unsync when playing)б mPacketDropped = 0, // Number of dropped packets (due to time unsync when playing)б
mIllegalRtp = 0; // Number of rtp packets with bad payload type mIllegalRtp = 0; // Number of rtp packets with bad payload type
TestResult<float> mDecodingInterval, // Average interval on call to packet decode TestResult<float> mDecodingInterval, // Average interval on call to packet decode
mDecodeRequested, // Average amount of requested audio frames to play mDecodeRequested, // Average amount of requested audio frames to play
mPacketInterval; // Average interval between packet adding to jitter buffer mPacketInterval; // Average interval between packet adding to jitter buffer
std::array<float, 128> mLoss = {0}; // Every item is number of loss of corresping length std::map<int,int> mLoss; // Every item is number of loss of corresping length
size_t mAudioTime = 0; // Decoded/found time in milliseconds size_t mAudioTime = 0; // Decoded/found time in milliseconds
size_t mDecodedSize = 0; // Number of decoded bytes size_t mDecodedSize = 0; // Number of decoded bytes
uint16_t mSsrc = 0; // Last known SSRC ID in a RTP stream uint16_t mSsrc = 0; // Last known SSRC ID in a RTP stream
ice::NetworkAddress mRemotePeer; // Last known remote RTP address ice::NetworkAddress mRemotePeer; // Last known remote RTP address
// AMR codec bitrate switch counter // AMR codec bitrate switch counter
int mBitrateSwitchCounter = 0; int mBitrateSwitchCounter = 0;
std::string mCodecName; int mCng = 0;
float mJitter = 0.0f; // Jitter std::string mCodecName;
TestResult<float> mRttDelay; // RTT delay float mJitter = 0.0f; // Jitter
TestResult<float> mRttDelay; // RTT delay
// Timestamp when first RTP packet has arrived // Timestamp when first RTP packet has arrived
std::optional<timepoint_t> mFirstRtpTime; std::optional<timepoint_t> mFirstRtpTime;
std::map<int, int> mCodecCount; // Stats on used codecs std::map<int, int> mCodecCount; // Stats on used codecs
std::vector<PacketLossEvent> mPacketLossTimeline; // Packet loss timeline std::vector<PacketLossEvent> mPacketLossTimeline; // Packet loss timeline
std::vector<Dtmf2833Event> mDtmf2833Timeline;
// It is to calculate network MOS // It is to calculate network MOS
void calculateBurstr(double* burstr, double* loss) const; void calculateBurstr(double* burstr, double* loss) const;

View File

@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.20)
project(rtp_decode)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(../../src build_rtphone)
add_executable(rtp_decode main.cpp)
target_link_libraries(rtp_decode PRIVATE rtphone)

291
test/rtp_decode/main.cpp Normal file
View File

@@ -0,0 +1,291 @@
/* Copyright(C) 2007-2026 VoIPobjects (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/. */
// rtp_decode — read an rtpdump file, decode RTP with a given codec, write WAV.
//
// Usage:
// rtp_decode <input.rtp> <output.wav> --codec <name> [--pt <N>] [--rate <N>] [--channels <N>]
#include "helper/HL_Rtp.h"
#include "media/MT_CodecList.h"
#include "media/MT_Codec.h"
#include "audio/Audio_WavFile.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <stdexcept>
// ---------------------------------------------------------------------------
// CLI helpers
// ---------------------------------------------------------------------------
static void usage(const char* progname)
{
fprintf(stderr,
"Usage: %s <input.rtp> <output.wav> --codec <name> [--pt <N>] [--rate <N>] [--channels <N>]\n"
"\n"
"Codecs: pcmu pcma g722 g729 opus gsm gsmhr gsmefr\n"
" amrnb amrwb amrnb-bwe amrwb-bwe evs ilbc20 ilbc30 isac16 isac32\n"
"\n"
"Options:\n"
" --codec <name> Codec name (required)\n"
" --pt <N> Override RTP payload type\n"
" --rate <N> Sample rate hint for Opus (default 48000)\n"
" --channels <N> Channel count hint for Opus (default 2)\n",
progname);
}
static const char* getOption(int argc, char* argv[], const char* name)
{
for (int i = 1; i < argc - 1; ++i) {
if (strcmp(argv[i], name) == 0)
return argv[i + 1];
}
return nullptr;
}
// ---------------------------------------------------------------------------
// Default payload types for codecs without a fixed standard PT
// ---------------------------------------------------------------------------
struct CodecDefaults
{
const char* name;
int defaultPt; // -1 = must be specified via --pt
bool needsPt; // true if --pt is required when no default exists
};
static const CodecDefaults kCodecTable[] = {
{ "pcmu", 0, false },
{ "pcma", 8, false },
{ "g722", 9, false },
{ "g729", 18, false },
{ "gsm", 3, false },
{ "opus", 106, false },
{ "amrnb", -1, true },
{ "amrwb", -1, true },
{ "amrnb-bwe", -1, true },
{ "amrwb-bwe", -1, true },
{ "gsmhr", -1, true },
{ "gsmefr", 126, false },
{ "evs", 127, false },
{ "ilbc20", -1, true },
{ "ilbc30", -1, true },
{ "isac16", -1, true },
{ "isac32", -1, true },
};
static const CodecDefaults* findCodecDefaults(const std::string& name)
{
for (auto& c : kCodecTable)
if (name == c.name)
return &c;
return nullptr;
}
// ---------------------------------------------------------------------------
// Build CodecList::Settings for the requested codec
// ---------------------------------------------------------------------------
static MT::CodecList::Settings buildSettings(const std::string& codecName, int pt,
int opusRate, int opusChannels)
{
MT::CodecList::Settings s;
if (codecName == "opus") {
s.mOpusSpec.push_back(MT::CodecList::Settings::OpusSpec(pt, opusRate, opusChannels));
} else if (codecName == "gsm") {
s.mGsmFrPayloadType = pt;
} else if (codecName == "gsmhr") {
s.mGsmHrPayloadType = pt;
} else if (codecName == "gsmefr") {
s.mGsmEfrPayloadType = pt;
} else if (codecName == "amrnb") {
s.mAmrNbOctetPayloadType.insert(pt);
} else if (codecName == "amrwb") {
s.mAmrWbOctetPayloadType.insert(pt);
} else if (codecName == "amrnb-bwe") {
s.mAmrNbPayloadType.insert(pt);
} else if (codecName == "amrwb-bwe") {
s.mAmrWbPayloadType.insert(pt);
} else if (codecName == "evs") {
MT::CodecList::Settings::EvsSpec ev;
ev.mPayloadType = pt;
s.mEvsSpec.push_back(ev);
} else if (codecName == "ilbc20") {
s.mIlbc20PayloadType = pt;
} else if (codecName == "ilbc30") {
s.mIlbc30PayloadType = pt;
} else if (codecName == "isac16") {
s.mIsac16KPayloadType = pt;
} else if (codecName == "isac32") {
s.mIsac32KPayloadType = pt;
}
// pcmu, pcma, g722, g729 — fixed PT, auto-registered by CodecList::init()
return s;
}
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
if (argc < 4) {
usage(argv[0]);
return 1;
}
const char* inputPath = argv[1];
const char* outputPath = argv[2];
const char* codecArg = getOption(argc, argv, "--codec");
if (!codecArg) {
fprintf(stderr, "Error: --codec is required\n\n");
usage(argv[0]);
return 1;
}
std::string codecName = codecArg;
const auto* defaults = findCodecDefaults(codecName);
if (!defaults) {
fprintf(stderr, "Error: unknown codec '%s'\n\n", codecArg);
usage(argv[0]);
return 1;
}
// Resolve payload type
int pt = defaults->defaultPt;
const char* ptArg = getOption(argc, argv, "--pt");
if (ptArg) {
pt = atoi(ptArg);
} else if (defaults->needsPt) {
fprintf(stderr, "Error: --pt is required for codec '%s'\n\n", codecArg);
usage(argv[0]);
return 1;
}
int opusRate = 48000;
int opusChannels = 2;
const char* rateArg = getOption(argc, argv, "--rate");
if (rateArg)
opusRate = atoi(rateArg);
const char* chArg = getOption(argc, argv, "--channels");
if (chArg)
opusChannels = atoi(chArg);
// -----------------------------------------------------------------------
// 1. Load rtpdump
// -----------------------------------------------------------------------
RtpDump dump(inputPath);
try {
dump.load();
} catch (const std::exception& e) {
fprintf(stderr, "Error loading rtpdump '%s': %s\n", inputPath, e.what());
return 1;
}
if (dump.count() == 0) {
fprintf(stderr, "No packets in '%s'\n", inputPath);
return 1;
}
fprintf(stderr, "Loaded %zu packets from '%s'\n", dump.count(), inputPath);
// -----------------------------------------------------------------------
// 2. Create codec
// -----------------------------------------------------------------------
auto settings = buildSettings(codecName, pt, opusRate, opusChannels);
MT::CodecList codecList(settings);
MT::PCodec codec = codecList.createCodecByPayloadType(pt);
if (!codec) {
fprintf(stderr, "Error: could not create codec for payload type %d\n", pt);
return 1;
}
auto codecInfo = codec->info();
fprintf(stderr, "Codec: %s samplerate=%d channels=%d pcmLength=%d frameTime=%dms\n",
codecInfo.mName.c_str(), codecInfo.mSamplerate, codecInfo.mChannels,
codecInfo.mPcmLength, codecInfo.mFrameTime);
// -----------------------------------------------------------------------
// 3. Open WAV writer
// -----------------------------------------------------------------------
Audio::WavFileWriter writer;
if (!writer.open(outputPath, codecInfo.mSamplerate, codecInfo.mChannels)) {
fprintf(stderr, "Error: could not open WAV file '%s' for writing\n", outputPath);
return 1;
}
// -----------------------------------------------------------------------
// 4. Decode loop
// -----------------------------------------------------------------------
std::vector<uint8_t> pcmBuffer(65536);
size_t totalDecodedBytes = 0;
size_t packetsDecoded = 0;
size_t packetsSkipped = 0;
for (size_t i = 0; i < dump.count(); ++i) {
const auto& rawData = dump.rawDataAt(i);
// Verify it's actually RTP
if (!RtpHelper::isRtp(rawData.data(), rawData.size())) {
++packetsSkipped;
continue;
}
// Parse RTP to get payload
jrtplib::RTPPacket& rtpPacket = dump.packetAt(i);
// Check payload type matches what we expect
int pktPt = rtpPacket.GetPayloadType();
if (pktPt != pt) {
++packetsSkipped;
continue;
}
uint8_t* payloadData = rtpPacket.GetPayloadData();
size_t payloadLen = rtpPacket.GetPayloadLength();
if (!payloadData || payloadLen == 0) {
++packetsSkipped;
continue;
}
std::span<const uint8_t> input(payloadData, payloadLen);
std::span<uint8_t> output(pcmBuffer.data(), pcmBuffer.size());
try {
auto result = codec->decode(input, output);
if (result.mDecoded > 0) {
writer.write(pcmBuffer.data(), result.mDecoded);
totalDecodedBytes += result.mDecoded;
++packetsDecoded;
}
} catch (const std::exception& e) {
fprintf(stderr, "Warning: decode error at packet %zu: %s\n", i, e.what());
++packetsSkipped;
}
}
// -----------------------------------------------------------------------
// 5. Close WAV and print summary
// -----------------------------------------------------------------------
writer.close();
size_t totalSamples = totalDecodedBytes / (sizeof(int16_t) * codecInfo.mChannels);
double durationSec = (codecInfo.mSamplerate > 0)
? static_cast<double>(totalSamples) / codecInfo.mSamplerate
: 0.0;
fprintf(stderr, "\nDone.\n");
fprintf(stderr, " Packets decoded: %zu\n", packetsDecoded);
fprintf(stderr, " Packets skipped: %zu\n", packetsSkipped);
fprintf(stderr, " Decoded PCM: %zu bytes\n", totalDecodedBytes);
fprintf(stderr, " Duration: %.3f seconds\n", durationSec);
fprintf(stderr, " Output: %s\n", outputPath);
return 0;
}