forked from mirrors/linphone-iphone
326 lines
12 KiB
C++
326 lines
12 KiB
C++
/*
|
|
* payload-type-handler.cpp
|
|
* Copyright (C) 2010-2017 Belledonne Communications SARL
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <cmath>
|
|
|
|
#include "private.h"
|
|
|
|
#include "logger/logger.h"
|
|
|
|
#include "payload-type-handler.h"
|
|
|
|
// =============================================================================
|
|
|
|
using namespace std;
|
|
|
|
LINPHONE_BEGIN_NAMESPACE
|
|
|
|
const int PayloadTypeHandler::udpHeaderSize = 8;
|
|
const int PayloadTypeHandler::rtpHeaderSize = 12;
|
|
|
|
// 20 is the minimum, but there may be some options.
|
|
const int PayloadTypeHandler::ipv4HeaderSize = 20;
|
|
|
|
const VbrCodecBitrate PayloadTypeHandler::defaultVbrCodecBitrates[] = {
|
|
{ 64, 44100, 50 },
|
|
{ 64, 16000, 40 },
|
|
{ 32, 16000, 32 },
|
|
{ 32, 8000, 32 },
|
|
{ 0, 8000, 24 },
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
// =============================================================================
|
|
|
|
int PayloadTypeHandler::findPayloadTypeNumber (const bctbx_list_t *assigned, const OrtpPayloadType *pt) {
|
|
const OrtpPayloadType *candidate = nullptr;
|
|
for (const bctbx_list_t *elem = assigned; elem != nullptr; elem = bctbx_list_next(elem)) {
|
|
const OrtpPayloadType *it = reinterpret_cast<const OrtpPayloadType *>(bctbx_list_get_data(elem));
|
|
if (
|
|
strcasecmp(pt->mime_type, payload_type_get_mime(it)) == 0 &&
|
|
it->clock_rate == pt->clock_rate &&
|
|
((it->channels == pt->channels) || (pt->channels <= 0))
|
|
) {
|
|
candidate = it;
|
|
if (
|
|
(it->recv_fmtp && pt->recv_fmtp && (strcasecmp(it->recv_fmtp, pt->recv_fmtp) == 0)) ||
|
|
(!it->recv_fmtp && !pt->recv_fmtp)
|
|
)
|
|
// Exact match.
|
|
break;
|
|
}
|
|
}
|
|
return candidate ? payload_type_get_number(candidate) : -1;
|
|
}
|
|
|
|
bool PayloadTypeHandler::hasTelephoneEventPayloadType (const bctbx_list_t *tev, int rate) {
|
|
for (const bctbx_list_t *it = tev; it != nullptr; it = bctbx_list_next(it)) {
|
|
const OrtpPayloadType *pt = reinterpret_cast<OrtpPayloadType *>(bctbx_list_get_data(it));
|
|
if (pt->clock_rate == rate)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PayloadTypeHandler::isPayloadTypeUsableForBandwidth (const OrtpPayloadType *pt, int bandwidthLimit) {
|
|
const int videoEnablementLimit = 99;
|
|
double codecBand = 0;
|
|
switch (pt->type) {
|
|
case PAYLOAD_AUDIO_CONTINUOUS:
|
|
case PAYLOAD_AUDIO_PACKETIZED:
|
|
codecBand = getAudioPayloadTypeBandwidth(pt, bandwidthLimit);
|
|
return bandwidthIsGreater(bandwidthLimit, (int)codecBand);
|
|
case PAYLOAD_VIDEO:
|
|
// Infinite or greater than videoEnablementLimit.
|
|
if (bandwidthLimit <= 0 || bandwidthLimit >= videoEnablementLimit)
|
|
return true;
|
|
break;
|
|
case PAYLOAD_TEXT:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int PayloadTypeHandler::lookupTypicalVbrBitrate (int maxBandwidth, int clockRate) {
|
|
if (maxBandwidth <= 0)
|
|
maxBandwidth = defaultVbrCodecBitrates[0].maxAvailableBitrate;
|
|
for (const VbrCodecBitrate *it = &defaultVbrCodecBitrates[0]; it->minClockRate != 0; it++) {
|
|
if ((maxBandwidth >= it->maxAvailableBitrate) && (clockRate >= it->minClockRate))
|
|
return it->recommendedBitrate;
|
|
}
|
|
lError() << "lookupTypicalVbrBitrate(): should not happen";
|
|
return 32;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
void PayloadTypeHandler::assignPayloadTypeNumbers (const bctbx_list_t *codecs) {
|
|
OrtpPayloadType *red = nullptr;
|
|
OrtpPayloadType *t140 = nullptr;
|
|
|
|
for (const bctbx_list_t *elem = codecs; elem != nullptr; elem = bctbx_list_next(elem)) {
|
|
OrtpPayloadType *pt = reinterpret_cast<OrtpPayloadType *>(bctbx_list_get_data(elem));
|
|
int number = payload_type_get_number(pt);
|
|
|
|
// Check if number is duplicated: it could be the case if the remote forced us to use a mapping with a previous offer.
|
|
if ((number != -1) && !(pt->flags & PAYLOAD_TYPE_FROZEN_NUMBER)) {
|
|
if (!isPayloadTypeNumberAvailable(codecs, number, pt)) {
|
|
lInfo() << "Reassigning payload type " << number << " " << pt->mime_type << "/" << pt->clock_rate << " because already offered";
|
|
// Need to be re-assigned.
|
|
number = -1;
|
|
}
|
|
}
|
|
|
|
if (number == -1) {
|
|
int dynNumber = getCore()->getCCore()->codecs_conf.dyn_pt;
|
|
while (dynNumber < 127) {
|
|
if (isPayloadTypeNumberAvailable(codecs, dynNumber, nullptr)) {
|
|
payload_type_set_number(pt, dynNumber);
|
|
dynNumber++;
|
|
break;
|
|
}
|
|
dynNumber++;
|
|
}
|
|
if (dynNumber == 127) {
|
|
lError() << "Too many payload types configured ! codec " << pt->mime_type << "/" << pt->clock_rate << " is disabled";
|
|
payload_type_set_enable(pt, false);
|
|
}
|
|
}
|
|
|
|
if (strcmp(pt->mime_type, payload_type_t140_red.mime_type) == 0)
|
|
red = pt;
|
|
else if (strcmp(pt->mime_type, payload_type_t140.mime_type) == 0)
|
|
t140 = pt;
|
|
}
|
|
|
|
if (t140 && red) {
|
|
int t140_payload_type_number = payload_type_get_number(t140);
|
|
char *red_fmtp = ms_strdup_printf("%i/%i/%i", t140_payload_type_number, t140_payload_type_number, t140_payload_type_number);
|
|
payload_type_set_recv_fmtp(red, red_fmtp);
|
|
ms_free(red_fmtp);
|
|
}
|
|
}
|
|
|
|
bctbx_list_t *PayloadTypeHandler::createSpecialPayloadTypes (const bctbx_list_t *codecs) {
|
|
bctbx_list_t *result = createTelephoneEventPayloadTypes(codecs);
|
|
if (linphone_core_generic_comfort_noise_enabled(getCore()->getCCore())) {
|
|
OrtpPayloadType *cn = payload_type_clone(&payload_type_cn);
|
|
payload_type_set_number(cn, 13);
|
|
result = bctbx_list_append(result, cn);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bctbx_list_t *PayloadTypeHandler::createTelephoneEventPayloadTypes (const bctbx_list_t *codecs) {
|
|
bctbx_list_t *result = nullptr;
|
|
for (const bctbx_list_t *it = codecs; it != nullptr; it = bctbx_list_next(it)) {
|
|
const OrtpPayloadType *pt = reinterpret_cast<OrtpPayloadType *>(bctbx_list_get_data(it));
|
|
if (hasTelephoneEventPayloadType(result, pt->clock_rate))
|
|
continue;
|
|
|
|
OrtpPayloadType *tev = payload_type_clone(&payload_type_telephone_event);
|
|
tev->clock_rate = pt->clock_rate;
|
|
// Let it choose the number dynamically as for normal codecs.
|
|
payload_type_set_number(tev, -1);
|
|
// But for first telephone-event, prefer the number that was configured in the core.
|
|
if (!result && isPayloadTypeNumberAvailable(codecs, getCore()->getCCore()->codecs_conf.telephone_event_pt, nullptr))
|
|
payload_type_set_number(tev, getCore()->getCCore()->codecs_conf.telephone_event_pt);
|
|
result = bctbx_list_append(result, tev);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool PayloadTypeHandler::isPayloadTypeUsable (const OrtpPayloadType *pt) {
|
|
return isPayloadTypeUsableForBandwidth(
|
|
pt, getMinBandwidth(
|
|
linphone_core_get_download_bandwidth(getCore()->getCCore()),
|
|
linphone_core_get_upload_bandwidth(getCore()->getCCore())
|
|
)
|
|
);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
bool PayloadTypeHandler::bandwidthIsGreater (int bandwidth1, int bandwidth2) {
|
|
if (bandwidth1 <= 0) return true;
|
|
if (bandwidth2 <= 0) return false;
|
|
return bandwidth1 >= bandwidth2;
|
|
}
|
|
|
|
int PayloadTypeHandler::getAudioPayloadTypeBandwidth (const OrtpPayloadType *pt, int maxBandwidth) {
|
|
if (payload_type_is_vbr(pt)) {
|
|
if (pt->flags & PAYLOAD_TYPE_BITRATE_OVERRIDE) {
|
|
lDebug() << "PayloadType " << pt->mime_type << "/" << pt->clock_rate << " has bitrate override";
|
|
return pt->normal_bitrate / 1000;
|
|
}
|
|
return lookupTypicalVbrBitrate(maxBandwidth, pt->clock_rate);
|
|
}
|
|
/* Rounding codec bandwidth should be avoid, specially for AMR */
|
|
return (int)ceil(getAudioPayloadTypeBandwidthFromCodecBitrate(pt) / 1000.0);
|
|
}
|
|
|
|
/*
|
|
*((codec-birate*ptime/8) + RTP header + UDP header + IP header)*8/ptime;
|
|
* ptime=1/npacket
|
|
*/
|
|
double PayloadTypeHandler::getAudioPayloadTypeBandwidthFromCodecBitrate (const OrtpPayloadType *pt) {
|
|
double npacket = 50;
|
|
if (strcmp(payload_type_get_mime(&payload_type_aaceld_44k), payload_type_get_mime(pt)) == 0)
|
|
// Special case of aac 44K because ptime=10ms.
|
|
npacket = 100;
|
|
else if (strcmp(payload_type_get_mime(&payload_type_ilbc), payload_type_get_mime(pt)) == 0)
|
|
npacket = 1000 / 30.0;
|
|
|
|
int bitrate = pt->normal_bitrate;
|
|
double packet_size = (((double)bitrate) / (npacket * 8)) + udpHeaderSize + rtpHeaderSize + ipv4HeaderSize;
|
|
return packet_size * 8.0 * npacket;
|
|
}
|
|
|
|
int PayloadTypeHandler::getMaxCodecSampleRate (const bctbx_list_t *codecs) {
|
|
int maxSampleRate = 0;
|
|
for (const bctbx_list_t *it = codecs; it != nullptr; it = bctbx_list_next(it)) {
|
|
OrtpPayloadType *pt = reinterpret_cast<OrtpPayloadType *>(bctbx_list_get_data(it));
|
|
int sampleRate;
|
|
if (strcasecmp("G722", pt->mime_type) == 0)
|
|
// G722 spec says 8000 but the codec actually requires 16000.
|
|
sampleRate = 16000;
|
|
else
|
|
sampleRate = pt->clock_rate;
|
|
|
|
if (sampleRate > maxSampleRate)
|
|
maxSampleRate = sampleRate;
|
|
}
|
|
return maxSampleRate;
|
|
}
|
|
|
|
int PayloadTypeHandler::getMinBandwidth (int downBandwidth, int upBandwidth) {
|
|
if (downBandwidth <= 0) return upBandwidth;
|
|
if (upBandwidth <= 0) return downBandwidth;
|
|
return MIN(downBandwidth, upBandwidth);
|
|
}
|
|
|
|
int PayloadTypeHandler::getRemainingBandwidthForVideo (int total, int audio) {
|
|
int remaining = total - audio - 10;
|
|
if (remaining < 0) remaining = 0;
|
|
return remaining;
|
|
}
|
|
|
|
bool PayloadTypeHandler::isPayloadTypeNumberAvailable (const bctbx_list_t *codecs, int number, const OrtpPayloadType *ignore) {
|
|
for (const bctbx_list_t *elem = codecs; elem != nullptr; elem = bctbx_list_next(elem)) {
|
|
const OrtpPayloadType *pt = reinterpret_cast<OrtpPayloadType *>(bctbx_list_get_data(elem));
|
|
if ((pt != ignore) && (payload_type_get_number(pt) == number)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
bctbx_list_t *PayloadTypeHandler::makeCodecsList (SalStreamType type, int bandwidthLimit, int maxCodecs, const bctbx_list_t *previousList) {
|
|
const bctbx_list_t *allCodecs = nullptr;
|
|
switch (type) {
|
|
default:
|
|
case SalAudio:
|
|
allCodecs = getCore()->getCCore()->codecs_conf.audio_codecs;
|
|
break;
|
|
case SalVideo:
|
|
allCodecs = getCore()->getCCore()->codecs_conf.video_codecs;
|
|
break;
|
|
case SalText:
|
|
allCodecs = getCore()->getCCore()->codecs_conf.text_codecs;
|
|
break;
|
|
}
|
|
|
|
int nb = 0;
|
|
bctbx_list_t *result = nullptr;
|
|
for (const bctbx_list_t *it = allCodecs; it != nullptr; it = bctbx_list_next(it)) {
|
|
OrtpPayloadType *pt = reinterpret_cast<OrtpPayloadType *>(bctbx_list_get_data(it));
|
|
if (!payload_type_enabled(pt))
|
|
continue;
|
|
|
|
if (bandwidthLimit > 0 && !isPayloadTypeUsableForBandwidth(pt, bandwidthLimit)) {
|
|
lInfo() << "Codec " << pt->mime_type << "/" << pt->clock_rate << " eliminated because of audio bandwidth constraint of " <<
|
|
bandwidthLimit << " kbit/s";
|
|
continue;
|
|
}
|
|
|
|
if (!isPayloadTypeUsable(pt))
|
|
continue;
|
|
|
|
pt = payload_type_clone(pt);
|
|
|
|
/* Look for a previously assigned number for this codec */
|
|
int num = findPayloadTypeNumber(previousList, pt);
|
|
if (num != -1) {
|
|
payload_type_set_number(pt, num);
|
|
payload_type_set_flag(pt, PAYLOAD_TYPE_FROZEN_NUMBER);
|
|
}
|
|
|
|
result = bctbx_list_append(result, pt);
|
|
nb++;
|
|
if ((maxCodecs > 0) && (nb >= maxCodecs)) break;
|
|
}
|
|
if (type == SalAudio) {
|
|
bctbx_list_t *specials = createSpecialPayloadTypes(result);
|
|
result = bctbx_list_concat(result, specials);
|
|
}
|
|
assignPayloadTypeNumbers(result);
|
|
return result;
|
|
}
|
|
|
|
LINPHONE_END_NAMESPACE
|