1 /*!
2  * \file        sccp_codec.c
3  * \brief       SCCP Codec Class
4  * \author      Diederik de Groot < ddegroot@users.sourceforge.net >
5  * \note        This program is free software and may be modified and distributed under the terms of the GNU Public License.
6  *              See the LICENSE file at the top of the source tree.
7  */
8 #include "config.h"
9 #include "common.h"
10 #include "sccp_codec.h"
11 #include "sccp_utils.h"
12 #include "sccp_channel.h"
13 
14 /*
15  * references: https://en.wikipedia.org/wiki/RTP_audio_video_profile
16  * --DD-- We need an extra column, specifying the RFC number, needed for MultiMediaReceive/MultiMediaTransmission->lel_payload_rfc_number;
17  */
18 const struct skinny_codec skinny_codecs[] = {
19 	/* clang-format off */
20 	{SKINNY_CODEC_NONE,		SKINNY_CODEC_TYPE_UNKNOWN, 	"",		"",		"No codec", 				NULL,		0,	0,	-1},	//payload unknown
21 	{SKINNY_CODEC_NONSTANDARD,	SKINNY_CODEC_TYPE_UNKNOWN,	"",		"",		"Non-standard codec", 			NULL,		0,	0,	-1},	//payload unknown
22 	{SKINNY_CODEC_IS11172,		SKINNY_CODEC_TYPE_AUDIO,	"is11172",	"is11172",	"IS11172 AudioCap", 			NULL,		8000,	1,	0},	//payload unknown
23 	{SKINNY_CODEC_IS13818,		SKINNY_CODEC_TYPE_AUDIO,	"is13872",      "is13872",	"IS13818 AudioCap", 			NULL,		8000,	1,	0},	//payload unknown
24 	{SKINNY_CODEC_GSM_FULLRATE,	SKINNY_CODEC_TYPE_AUDIO, 	"gsm",		"gsm/full",	"GSM Full Rate", 			"GSM",		8000,	2,	3},
25 	{SKINNY_CODEC_GSM_HALFRATE,	SKINNY_CODEC_TYPE_AUDIO, 	"gsm",		"gsm/half",	"GSM Half Rate", 			"GSM-HR-08",	8000,	1,	3},
26 	{SKINNY_CODEC_GSM_ENH_FULLRATE,	SKINNY_CODEC_TYPE_AUDIO,	"gsm",		"gsm/enh",	"GSM Enhanced Full Rate",		"GSM-EFR",	8000,	2,	3},
27 	//{SKINNY_CODEC_WIDEBAND_256K,	SKINNY_CODEC_TYPE_AUDIO, 	"slin16",	"slin16",	"Wideband 1411.2 kbit/s",		"L16",		44100,	3,	10},
28 	//{SKINNY_CODEC_WIDEBAND_256K,	SKINNY_CODEC_TYPE_AUDIO, 	"slin16",	"slin16",	"Wideband 705.6 kbit/s", 		"L16",		44100,	3,	11},
29 	{SKINNY_CODEC_WIDEBAND_256K,	SKINNY_CODEC_TYPE_AUDIO, 	"slin16",	"slin16",	"Wideband 256 kbit/k",	 		"L16",		44100,	3,	25},
30 	{SKINNY_CODEC_GSM,		SKINNY_CODEC_TYPE_AUDIO, 	"gsm",		"gsm",		"GSM", 					"GSM",		8000,	1,	3},
31 	{SKINNY_CODEC_ACTIVEVOICE,	SKINNY_CODEC_TYPE_AUDIO, 	"activevoice",	"activevoice",	"ActiveVoice", 				NULL,		8000,	1,	-1},	//payload unknown
32 	{SKINNY_CODEC_G711_ALAW_64K,	SKINNY_CODEC_TYPE_AUDIO, 	"alaw",		"alaw/64k",	"G.711 A-law 64k", 			"PCMA",		8000,	2,	8},
33 	{SKINNY_CODEC_G711_ALAW_56K,	SKINNY_CODEC_TYPE_AUDIO, 	"alaw",		"alaw/56k",	"G.711 A-law 56k", 			"PCMA",		8000,	1,	8},	// --DD-- which one ?
34 	{SKINNY_CODEC_G711_ULAW_64K,	SKINNY_CODEC_TYPE_AUDIO, 	"ulaw",		"ulaw/64k",	"G.711 u-law 64k", 			"PCMU",		8000,	2,	0},
35 	{SKINNY_CODEC_G711_ULAW_56K,	SKINNY_CODEC_TYPE_AUDIO, 	"ulaw",		"ulaw/56k",	"G.711 u-law 56k", 			"PCMU",		8000,	1,	0},	// --DD-- which one ?
36 	{SKINNY_CODEC_G722_64K,		SKINNY_CODEC_TYPE_AUDIO, 	"g722",		"g722/64k",	"G.722 64k", 				"G722",		8000,	3,	9},
37 	{SKINNY_CODEC_G722_56K,		SKINNY_CODEC_TYPE_AUDIO, 	"g722",		"g722/56k",	"G.722 56k", 				"G722",		8000,	3,	9},
38 	{SKINNY_CODEC_G722_48K,		SKINNY_CODEC_TYPE_AUDIO, 	"g722",		"g722/48k",	"G.722 48k", 				"G722",		8000,	2,	9},	// --DD-- which one ?
39 	{SKINNY_CODEC_G722_1_24K,	SKINNY_CODEC_TYPE_AUDIO, 	"g722.1",	"g722.1/24k",	"G722.1 24k (Siren7)", 			"G7221",	16000,	3,	102},	// --DD-- please check
40 	{SKINNY_CODEC_G722_1_32K,	SKINNY_CODEC_TYPE_AUDIO, 	"g722.1",	"g722.1/32k",	"G722.1 32k (Siren14)", 		"G7221", 	32000,	4,	115},	// --DD-- please check
41 	{SKINNY_CODEC_G723_1,		SKINNY_CODEC_TYPE_AUDIO, 	"g723",		"g723",		"G.723.1", 				"G723",		8000,	1,	4},
42 	{SKINNY_CODEC_G726_16K,		SKINNY_CODEC_TYPE_AUDIO, 	"g726",		"g726/16k",	"G.726 16K", 			 	"G726-16",	8000,	1,	2},
43 	{SKINNY_CODEC_G726_24K,		SKINNY_CODEC_TYPE_AUDIO,	"g726",		"g726/24k",	"G.726 24K", 			 	"G726-24",	8000,	1,	2},
44 	{SKINNY_CODEC_G726_32K,		SKINNY_CODEC_TYPE_AUDIO, 	"g726",		"g726/32k",	"G.726 32K", 			 	"G726-32",	8000,	1,	112},	//payload could also be 111
45 	{SKINNY_CODEC_G728,		SKINNY_CODEC_TYPE_AUDIO,	"g728",		"g728",		"G.728", 			 	"G728",		8000,	1,	-1},	//payload unknown
46 	{SKINNY_CODEC_G729,		SKINNY_CODEC_TYPE_AUDIO,	"g729",		"g729",		"G.729", 				"G729",		8000,	1,	18},
47 	{SKINNY_CODEC_G729_A,		SKINNY_CODEC_TYPE_AUDIO,	"g729",		"g729a",	"G.729 Annex A", 			"G729a",	8000,	1,	18},
48 	{SKINNY_CODEC_G729_B_LOW,	SKINNY_CODEC_TYPE_AUDIO, 	"ilbc",		"ilbc",		"G.729B Low/ILBC",			"iLBC",		8000,	1,	97},
49 	{SKINNY_CODEC_G729_B,		SKINNY_CODEC_TYPE_AUDIO,	"g729",		"g729b",	"G.729 Annex B", 			"G729",		8000,	1,	18},
50 	{SKINNY_CODEC_G729_AB,		SKINNY_CODEC_TYPE_AUDIO,	"g729",		"g729ab",	"G.729 Annex A + B", 			"G729",		8000,	1,	18},
51 	{SKINNY_CODEC_G729_ANNEX_B,	SKINNY_CODEC_TYPE_AUDIO, 	"g729",		"g729/annex/b",	"G.729 Annex B", 			"G729",		8000,	1,	18},
52 	{SKINNY_CODEC_ISAC,		SKINNY_CODEC_TYPE_AUDIO,	"isac",		"isac",		"iSAC", 				NULL,		8000,	1,	-1},	//payload unknown
53 	{SKINNY_CODEC_OPUS,		SKINNY_CODEC_TYPE_AUDIO,	"opus",		"opus",		"Opus", 				"opus",		16000,	1,	-2},	//new opus payload // --DD-- please check
54 	{SKINNY_CODEC_H224,		SKINNY_CODEC_TYPE_AUDIO,	"h224",		"h224",		"H.224", 				NULL,		8000,	1,	31},
55 	{SKINNY_CODEC_AAC,		SKINNY_CODEC_TYPE_AUDIO, 	"aac",		"aac",		"AAC", 					NULL,		8000,	1,	-1},
56 	{SKINNY_CODEC_MP4A_LATM_128,	SKINNY_CODEC_TYPE_AUDIO,	"mp4a_latm_128","mp4a_latm_128","mp4a latm 128k",			"MP4A-LATM",	128000,	1,	-1},
57 	{SKINNY_CODEC_MP4A_LATM_64,	SKINNY_CODEC_TYPE_AUDIO,	"mp4a_latm_64",	"mp4a_latm_64",	"mp4a latm 64k",			"MP4A-LATM",	64000,	1,	-1},
58 	{SKINNY_CODEC_MP4A_LATM_56,	SKINNY_CODEC_TYPE_AUDIO,	"mp4a_latm_56",	"mp4a_latm_56",	"mp4a latm 56k",			"MP4A-LATM",	56000,	1,	-1},
59 	{SKINNY_CODEC_MP4A_LATM_48,	SKINNY_CODEC_TYPE_AUDIO,	"mp4a_latm_48",	"mp4a_latm_48",	"mp4a latm 48k",			"MP4A-LATM",	48000,	1,	-1},
60 	{SKINNY_CODEC_MP4A_LATM_32,	SKINNY_CODEC_TYPE_AUDIO,	"mp4a_latm_32",	"mp4a_latm_32",	"mp4a latm 32k",			"MP4A-LATM",	32000,	1,	-1},
61 	{SKINNY_CODEC_MP4A_LATM_24,	SKINNY_CODEC_TYPE_AUDIO,	"mp4a_latm_24",	"mp4a_latm_24",	"mp4a latm 24k",			"MP4A-LATM",	24000,	1,	-1},
62 	{SKINNY_CODEC_MP4A_LATM_NA,	SKINNY_CODEC_TYPE_AUDIO,	"mp4a_latm_na",	"mp4a_latm_na",	"mp4a latm nak",			"MP4A-LATM",	8000,	1,	-1},
63 	{SKINNY_CODEC_AMR,		SKINNY_CODEC_TYPE_AUDIO,	"amr",		"amr",		"amr",					"AMR",		8000,	1,	-2},	// --DD-- please check
64 	{SKINNY_CODEC_AMR_WB,		SKINNY_CODEC_TYPE_AUDIO,	"amr_wb",	"amr_wb",	"amr_wb",				"AMR-WB",	16000,	1,	-2},	// --DD-- please check
65 	{SKINNY_CODEC_H261,		SKINNY_CODEC_TYPE_VIDEO,	"h261",		"h261",		"H.261", 				"H261",		90000,	1,	34},
66 	{SKINNY_CODEC_H263,		SKINNY_CODEC_TYPE_VIDEO,	"h263",		"h263",		"H.263", 				"H263",		90000,	1,	34},
67 	{SKINNY_CODEC_H263P,		SKINNY_CODEC_TYPE_VIDEO,	"h263",		"h263p",	"Vieo H.263+", 				"H263p",	90000,	1,	98},
68 	{SKINNY_CODEC_H264,		SKINNY_CODEC_TYPE_VIDEO,	"h264",		"h264",		"H.264", 				"H264",		90000,	1,	103},
69 	{SKINNY_CODEC_H264_SVC,		SKINNY_CODEC_TYPE_VIDEO,	"h264",		"h264_svc",	"h264_svc",				"H264 SVC",	90000,	1,	-2},	// --DD-- please check
70 	{SKINNY_CODEC_H265,		SKINNY_CODEC_TYPE_VIDEO,	"h265",		"h265",		"h265",					"H265",		90000,	1,	-1},
71 	{SKINNY_CODEC_H264_FEC,		SKINNY_CODEC_TYPE_VIDEO,	"h264",		"h264f",	"h264_fec", 				NULL,		90000,	1,	-2},	//unknown codec type --DD-- please check
72 	{SKINNY_CODEC_H264_UC,		SKINNY_CODEC_TYPE_VIDEO,	"h264",		"h264uc",	"h264_uc",				NULL,		90000,	1,	-2},	// --DD-- please check
73 	{SKINNY_CODEC_T120,		SKINNY_CODEC_TYPE_TEXT, 	"t120",		"t120",		"T.140", 				NULL,		1000,	1,	-2},	//payload unknown --DD--
74 	{SKINNY_CODEC_DATA_64K,		SKINNY_CODEC_TYPE_DATA, 	"data",		"data/64k",	"Data 64k", 				NULL,		1000,	1,	-2},	//payload unknown
75 	{SKINNY_CODEC_DATA_56K,		SKINNY_CODEC_TYPE_DATA, 	"data",		"data/56k",	"Data 56k", 				NULL,		1000,	1,	-2},	//payload unknown
76 	{SKINNY_CODEC_H224,		SKINNY_CODEC_TYPE_DATA,		"h224",		"h224",		"h224, far-end camera control",		NULL,		1000,	1,	-2},	// --DD-- please check
77 	{SKINNY_CODEC_T38FAX,		SKINNY_CODEC_TYPE_DATA,		"t38fax",	"t38fax",	"T38 fax",				NULL,		1000,	1,	-2},	// --DD-- please check
78 	{SKINNY_CODEC_TOTE,		SKINNY_CODEC_TYPE_DATA,		"tote",		"tote",		"tote",					NULL,		1000,	1,	-1},	// unknown codec type
79 	{SKINNY_CODEC_XV150_MR_711U,	SKINNY_CODEC_TYPE_MIXED,	"xv711u",	"xv711u",	"xv150_mr_711u", 			NULL,		0,	1,	-1},	//used for modem traffic over vg224
80 	{SKINNY_CODEC_NSE_VBD_711U,	SKINNY_CODEC_TYPE_MIXED,	"v711u",	"v711u",	"nse vbd 711u", 			NULL,		0,	1,	-1},	//unknown codec type
81 	{SKINNY_CODEC_XV150_MR_729A,	SKINNY_CODEC_TYPE_MIXED,	"xv729a",	"xv729a",	"xv150_mr_711u", 			NULL,		0,	1,	-1},	//used for modem traffic over vg224
82 	{SKINNY_CODEC_NSE_VBD_729A,	SKINNY_CODEC_TYPE_MIXED,	"v729a",	"v729a",	"nse_vbd_729a", 			NULL,		0,	1,	-1},	//unknown codec type
83 	{SKINNY_CODEC_CLEAR_CHAN,	SKINNY_CODEC_TYPE_MIXED,	"clear_chan",	"clear_chan",	"clear_chan",				NULL,		0,	1,	-1},
84 	{SKINNY_CODEC_UNIVERSAL_XCODER,	SKINNY_CODEC_TYPE_MIXED,	"univ_xcoder",	"univ_xcoder", 	"univ_xcoder",				NULL,		0,	1,	-1},
85 	{SKINNY_CODEC_DTMF_OOB_RFC2833,	SKINNY_CODEC_TYPE_MIXED,	"rfc2833",	"rfc2833",	"DTMF RFC 2833 Dyn Pay Load",		"telephone-event",	0,	1,	101},	// --DD-- please check 105| 101| 96 which one goes where
86 	{SKINNY_CODEC_DTMF_PASSTHROUGH,	SKINNY_CODEC_TYPE_MIXED,	"passthrough",	"passthrough",	"DTMF Passthrough",			"CISCO_DTMF pt"	,0,	1,	105},	// --DD-- please check 105| 101| 96
87 	{SKINNY_CODEC_DTMF_DYNAMIC,	SKINNY_CODEC_TYPE_MIXED,	"dynamic",	"dynamic",	"DTMF Dynamic",				NULL,		0,	1,	101},	// --DD-- please check 105| 101| 96
88 	{SKINNY_CODEC_DTMF_OOB,		SKINNY_CODEC_TYPE_MIXED,	"oob",		"oob",		"DTMF Out of Band",			NULL,		0,	1,	101},	// --DD-- please check 105| 101| 96
89 	{SKINNY_CODEC_DTMF_IB_RFC2833,	SKINNY_CODEC_TYPE_MIXED,	"rfc2833(ib)",	"rfc2833(ib)",	"DTMF RFC2833 In band",			"CISCO_DTMF ib",0,	1,	96},	// --DD-- please check 105| 101| 96
90 	{SKINNY_CODEC_CFB_TONES,	SKINNY_CODEC_TYPE_MIXED,	"cfb",		"cfb",		"CFB Tones",				NULL,		0,	1,	-1},
91 	{SKINNY_CODEC_DTMF_NOAUDIO,	SKINNY_CODEC_TYPE_MIXED,	"noaudio",	"noaudio",	"DTMF NoAudio",				NULL,		0,	1,	-1},
92 	{SKINNY_CODEC_V150_LC_MODEM_RELAY,SKINNY_CODEC_TYPE_MIXED,	"v150_modem",	"v150_modem", 	"v150_lc_modem_relay",			NULL,		0,	1,	-1},
93 	{SKINNY_CODEC_V150_LC_SPRT,	SKINNY_CODEC_TYPE_MIXED,	"v150_sprt",	"v150_sprt",	"v150_lc_sprt",				NULL,		0,	1,	-1},
94 	{SKINNY_CODEC_V150_LC_SSE,	SKINNY_CODEC_TYPE_MIXED,	"v150_sse",	"v150_sse",	"v150_lc_sse",				NULL,		0,	1,	-1},
95 	/* clang-format on */
96 };
97 
sccp_codec_getArrayLen()98 uint8_t __CONST__ sccp_codec_getArrayLen()
99 {
100 	return ARRAY_LEN(skinny_codecs);
101 }
codec2str(skinny_codec_t value)102 gcc_inline const char * codec2str(skinny_codec_t value)
103 {
104 	_ARR2STR(skinny_codecs, codec, value, text);
105 }
106 
codec2name(skinny_codec_t value)107 gcc_inline const char * codec2name(skinny_codec_t value)
108 {
109 	_ARR2STR(skinny_codecs, codec, value, name);
110 }
111 
112 /*! \todo should be called skinny_codec_type_t instead of skinny_payload_type_t */
codec2type(skinny_codec_t value)113 gcc_inline const skinny_payload_type_t codec2type(skinny_codec_t value)
114 {
115 	uint32_t i = 0;
116 	for (i = 0; i < ARRAY_LEN(skinny_codecs); i++) {
117 		if (skinny_codecs[i].codec == value) {
118 			return skinny_codecs[i].codec_type;
119 		}
120 	}
121 	pbx_log(LOG_ERROR, "codec2type lookup failed for skinny_codecs[%i]\n", value);
122 	return SKINNY_CODEC_TYPE_UNKNOWN;
123 }
124 
codec2rtp_payload_type(skinny_codec_t value)125 gcc_inline const int32_t codec2rtp_payload_type(skinny_codec_t value)
126 {
127 	uint32_t i = 0;
128 	for (i = 0; i < ARRAY_LEN(skinny_codecs); i++) {
129 		if (skinny_codecs[i].codec == value) {
130 			return skinny_codecs[i].rtp_payload_type;
131 		}
132 	}
133 	pbx_log(LOG_ERROR, "codec2rtp_payload_type lookup failed for skinny_codecs[%i]\n", value);
134 	return SKINNY_CODEC_TYPE_UNKNOWN;
135 }
136 
137 /*!
138  * \brief Retrieve the string of format numbers and names from an array of formats
139  * Buffer needs to be declared and freed afterwards
140 
141  * \param buf   Buffer as char array
142  * \param size  Size of Buffer
143  * \param codecs Array of Skinny Codecs
144  * \param clength Max Length
145  */
sccp_codec_multiple2str(char * buf,size_t size,const skinny_codec_t * codecs,const uint clength)146 char * sccp_codec_multiple2str(char * buf, size_t size, const skinny_codec_t * codecs, const uint clength)
147 {
148 	if (!buf || size <= 2) {
149 		return buf;
150 	}
151 	memset(buf, 0, size);
152 	char * endptr = buf;
153 	int    comma  = 0;
154 
155 	snprintf(endptr++, size, "[");
156 	endptr += strlen(endptr);
157 	for (uint x = 0; x < clength; x++) {
158 		if (codecs[x] == SKINNY_CODEC_NONE || codecs[x] == SKINNY_CODEC_NONSTANDARD) {
159 			break;
160 		}
161 		// snprintf(endptr, size, "%s%s (%d)", comma++ ? ", " : "",codec2name(codecs[x]), codecs[x]);
162 		snprintf(endptr, size, "%s%s", comma++ ? ", " : "", codec2name(codecs[x]));
163 		endptr += strlen(endptr);
164 	}
165 	if (buf == endptr) {
166 		snprintf(endptr, size, "nothing)");
167 	}
168 	snprintf(endptr++, size, "]");
169 	return buf;
170 }
171 
codec_pref_remove(skinny_codec_t * skinny_codec_prefs,skinny_codec_t skinny_codec)172 static void codec_pref_remove(skinny_codec_t * skinny_codec_prefs, skinny_codec_t skinny_codec)
173 {
174 	int       x     = 0;
175 	boolean_t found = FALSE;
176 
177 	for (x = 0; x < SKINNY_MAX_CAPABILITIES && skinny_codec_prefs[x] != SKINNY_CODEC_NONE; x++) {
178 		if (!found && skinny_codec_prefs[x] == skinny_codec) {
179 			found = TRUE;
180 		}
181 		if (found) {
182 			memmove(skinny_codec_prefs + x, skinny_codec_prefs + (x + 1), (SKINNY_MAX_CAPABILITIES - (x + 1)) * sizeof(skinny_codec_t));                                        // move left
183 		}
184 	}
185 }
186 
187 /*!
188  * \brief Append Codec to Skinny Codec Preferences
189  */
codec_pref_append(skinny_codec_t * skinny_codec_pref,skinny_codec_t skinny_codec)190 static int codec_pref_append(skinny_codec_t * skinny_codec_pref, skinny_codec_t skinny_codec)
191 {
192 	int x = 0;
193 
194 	// append behaviour: remove old entry, move all other entries left, append
195 	codec_pref_remove(skinny_codec_pref, skinny_codec);
196 
197 	for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
198 		if (SKINNY_CODEC_NONE == skinny_codec_pref[x]) {
199 			// sccp_log((DEBUGCAT_CODEC)) (VERBOSE_PREFIX_1 "inserting codec '%s (%d)' at pos %d\n", codec2name(skinny_codec), skinny_codec, x);
200 			skinny_codec_pref[x] = skinny_codec;
201 			return x;
202 		}
203 	}
204 	return -1;
205 }
206 
207 /*!
208  * \brief Parse Skinny Codec Allow / Disallow Config Lines
209  */
sccp_codec_parseAllowDisallow(skinny_codec_t * skinny_codec_prefs,const char * list,int allowing)210 int sccp_codec_parseAllowDisallow(skinny_codec_t * skinny_codec_prefs, const char * list, int allowing)
211 {
212 	int errors = 0;
213 
214 	if (!skinny_codec_prefs) {
215 		return -1;
216 	}
217 	unsigned int x     = 0;
218 	boolean_t    all   = FALSE;
219 	boolean_t    found = FALSE;
220 	boolean_t    allow = allowing;
221 	char *       parse = NULL;
222 
223 	char *         token = NULL;
224 	skinny_codec_t codec = 0;
225 
226 	parse = pbx_strdupa(list);
227 	while ((token = strsep(&parse, ","))) {
228 		if (!sccp_strlen_zero(token)) {
229 			if (token[0] == '!') {
230 				// sccp_log((DEBUGCAT_CODEC)) (VERBOSE_PREFIX_1 "matched !, token=%s\n", token);
231 				allow = !allowing;
232 				token++;                                        // consume !
233 			}
234 			all = sccp_strcaseequals(token, "all") ? TRUE : FALSE;
235 			if (all && !allow) {                                        // disallowing all
236 				memset(skinny_codec_prefs, 0, sizeof(skinny_codec_t) * SKINNY_MAX_CAPABILITIES);
237 				// sccp_log((DEBUGCAT_CODEC)) ("SCCP: disallow=all => reset codecs\n");
238 				allow = allowing;
239 				continue;
240 			}
241 			for (x = 0; x < sccp_codec_getArrayLen(); x++) {
242 				if (all || sccp_strcaseequals(skinny_codecs[x].key, token)) {
243 					codec = skinny_codecs[x].codec;
244 					found = TRUE;
245 					if (allow) {
246 						// sccp_log((DEBUGCAT_CODEC)) (VERBOSE_PREFIX_1 "appending codec '%s'\n", codec2name(codec));
247 						codec_pref_append(skinny_codec_prefs, codec);
248 					} else {
249 						// sccp_log((DEBUGCAT_CODEC)) (VERBOSE_PREFIX_1 "removing codec '%s'\n", codec2name(codec));
250 						codec_pref_remove(skinny_codec_prefs, codec);
251 					}
252 				}
253 			}
254 			allow = allowing;
255 			if (!found) {
256 				// pbx_log(LOG_WARNING, "Cannot %s unknown codec '%s'\n", allow ? "allow" : "disallow", token);
257 				errors++;
258 			}
259 		}
260 	}
261 	return errors;
262 }
263 
sccp_get_codecs_bytype(const skinny_codec_t * in_codecs,skinny_codec_t * out_codecs,skinny_payload_type_t type)264 int sccp_get_codecs_bytype(const skinny_codec_t * in_codecs, skinny_codec_t * out_codecs, skinny_payload_type_t type)
265 {
266 	int x = 0;
267 
268 	int y = 0;
269 
270 	int z = 0;
271 	for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
272 		if (SKINNY_CODEC_NONE != in_codecs[x]) {
273 			for (y = 0; y < sccp_codec_getArrayLen(); y++) {
274 				if (skinny_codecs[y].codec == in_codecs[x] && skinny_codecs[y].codec_type == type) {
275 					// sccp_log((DEBUGCAT_CODEC)) ("Adding codec:%s\n", skinny_codecs[y].name);
276 					out_codecs[z++] = skinny_codecs[y].codec;
277 				}
278 			}
279 		}
280 	}
281 	return z;
282 }
283 
284 /*!
285  * \brief Check if Skinny Codec is compatible with Skinny Capabilities Array
286  */
sccp_codec_isCompatible(skinny_codec_t codec,const skinny_codec_t capabilities[],uint8_t length)287 boolean_t __PURE__ sccp_codec_isCompatible(skinny_codec_t codec, const skinny_codec_t capabilities[], uint8_t length)
288 {
289 	for (uint8_t i = 0; i < length; i++) {
290 		if (capabilities[i] == SKINNY_CODEC_NONE) {
291 			break;
292 		}
293 		if (capabilities[i] == codec) {
294 			return TRUE;
295 		}
296 	}
297 	return FALSE;
298 }
299 
300 /*!
301  * \brief get smallest common denominator codecset
302  * intersection of two sets
303  */
sccp_codec_getReducedSet(const skinny_codec_t base[SKINNY_MAX_CAPABILITIES],const skinny_codec_t reduceByCodecs[SKINNY_MAX_CAPABILITIES],skinny_codec_t result[SKINNY_MAX_CAPABILITIES])304 int sccp_codec_getReducedSet(const skinny_codec_t base[SKINNY_MAX_CAPABILITIES], const skinny_codec_t reduceByCodecs[SKINNY_MAX_CAPABILITIES], skinny_codec_t result[SKINNY_MAX_CAPABILITIES])
305 {
306 	uint8_t x = 0;
307 
308 	uint8_t y = 0;
309 
310 	uint8_t z = 0;
311 	for (x = 0; x < SKINNY_MAX_CAPABILITIES && (z + 1) < SKINNY_MAX_CAPABILITIES && base[x] != SKINNY_CODEC_NONE; x++) {
312 		for (y = 0; y < SKINNY_MAX_CAPABILITIES && (z + 1) < SKINNY_MAX_CAPABILITIES && reduceByCodecs[y] != SKINNY_CODEC_NONE; y++) {
313 			if (base[x] == reduceByCodecs[y]) {
314 				result[z++] = base[x];
315 			}
316 		}
317 	}
318 	return z; /* no matches / overlap */
319 }
320 /*!
321  * \brief get smallest common denominator codecset
322  * intersection of two sets
323  */
sccp_codec_reduceSet(skinny_codec_t base[SKINNY_MAX_CAPABILITIES],const skinny_codec_t reduceByCodecs[SKINNY_MAX_CAPABILITIES])324 void sccp_codec_reduceSet(skinny_codec_t base[SKINNY_MAX_CAPABILITIES], const skinny_codec_t reduceByCodecs[SKINNY_MAX_CAPABILITIES])
325 {
326 	skinny_codec_t temp[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONE };
327 	sccp_codec_getReducedSet(base, reduceByCodecs, temp);
328 	memcpy(base, temp, sizeof(skinny_codec_t) * SKINNY_MAX_CAPABILITIES);
329 }
330 
331 /*!
332  * \brief combine two codecs sets skipping duplicates
333  * union of two sets
334  */
sccp_codec_combineSets(skinny_codec_t base[SKINNY_MAX_CAPABILITIES],const skinny_codec_t addCodecs[SKINNY_MAX_CAPABILITIES])335 void sccp_codec_combineSets(skinny_codec_t base[SKINNY_MAX_CAPABILITIES], const skinny_codec_t addCodecs[SKINNY_MAX_CAPABILITIES])
336 {
337 	uint8_t x = 0;
338 
339 	uint8_t y = 0;
340 
341 	uint8_t z = 0;
342 
343 	uint8_t demarquation = SKINNY_MAX_CAPABILITIES;
344 	for (y = 0; y < SKINNY_MAX_CAPABILITIES && addCodecs[y] != SKINNY_CODEC_NONE; y++) {
345 		boolean_t found = FALSE;
346 		for (x = 0; x < demarquation && base[x] != SKINNY_CODEC_NONE; x++) {
347 			if (base[x] == addCodecs[y]) {
348 				found = TRUE;
349 				break;
350 			}
351 		}
352 		while (!found && z < SKINNY_MAX_CAPABILITIES) {
353 			if (base[z] == SKINNY_CODEC_NONE) {
354 				if (demarquation) {                                        // don't check against newly added codecs
355 					demarquation = z;
356 				}
357 				base[z] = addCodecs[y];
358 				break;
359 			}
360 			z++;
361 		}
362 	}
363 }
364 
sccp_codec_findBestJoint(constChannelPtr c,const skinny_codec_t ourPreferences[],const skinny_codec_t remotePeerPreferences[],boolean_t fallback)365 skinny_codec_t sccp_codec_findBestJoint(constChannelPtr c, const skinny_codec_t ourPreferences[], const skinny_codec_t remotePeerPreferences[], boolean_t fallback)
366 {
367 	skinny_codec_t   res                                = SKINNY_CODEC_NONE;
368 	skinny_codec_t   leadPrefs[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONE };
369 	skinny_codec_t * followPrefs                        = NULL;
370 
371 	/* debug */
372 	// char pref_buf[256]; sccp_codec_multiple2str(pref_buf, sizeof(pref_buf) - 1, ourPreferences, SKINNY_MAX_CAPABILITIES);
373 	// char remote_buf[256]; sccp_codec_multiple2str(remote_buf, sizeof(remote_buf) - 1, remotePeerPreferences, SKINNY_MAX_CAPABILITIES);
374 	// sccp_log_and((DEBUGCAT_CODEC + DEBUGCAT_HIGH)) (VERBOSE_PREFIX_3 "ourPref:%s, remoteCap::%s, direction:%s\n", pref_buf, remote_buf, skinny_calltype2str(c->calltype));
375 	/* end debug */
376 
377 	if (ourPreferences[0] == SKINNY_CODEC_NONE && remotePeerPreferences[0] == SKINNY_CODEC_NONE) {
378 		sccp_log(DEBUGCAT_CODEC)(VERBOSE_PREFIX_3 "%s: both preference lists are empty\n", c->designator);
379 		goto EXIT;
380 	}
381 
382 	/* direction of the call determines who leads */
383 	/*if (SKINNY_CALLTYPE_INBOUND == c->calltype){
384 	        memcpy(leadPrefs, remotePeerPreferences, sizeof(skinny_codec_t) * SKINNY_MAX_CAPABILITIES);
385 	        followPrefs = (skinny_codec_t *) ourPreferences;
386 	} else {*/
387 	memcpy(leadPrefs, ourPreferences, sizeof(skinny_codec_t) * SKINNY_MAX_CAPABILITIES);
388 	followPrefs = (skinny_codec_t *)remotePeerPreferences;
389 	//}
390 
391 	sccp_codec_reduceSet(leadPrefs, followPrefs);
392 	res = leadPrefs[0];
393 
394 EXIT:
395 	if (res == SKINNY_CODEC_NONE && fallback) {
396 		sccp_log(DEBUGCAT_CODEC)(VERBOSE_PREFIX_3 "%s, Could not find a common prefered codec (yet), using %s (%d)\n", c->designator, codec2name(ourPreferences[0]), ourPreferences[0]);
397 		res = ourPreferences[0];
398 	}
399 	sccp_log(DEBUGCAT_CODEC)(VERBOSE_PREFIX_3 "%s: (findBestJoint) returning prefered codec %s (%d)\n", c->designator, codec2name(res), res);
400 	return res;
401 }
402 
403 #if CS_TEST_FRAMEWORK
404 #	include <asterisk/test.h>
AST_TEST_DEFINE(chan_sccp_reduce_codec_set)405 AST_TEST_DEFINE(chan_sccp_reduce_codec_set)
406 {
407 	switch (cmd) {
408 		case TEST_INIT:
409 			info->name        = "reduceCodecSet";
410 			info->category    = "/channels/chan_sccp/codec/";
411 			info->summary     = "reduceCodecSet unit test";
412 			info->description = "reduceCodecSet";
413 			return AST_TEST_NOT_RUN;
414 		case TEST_EXECUTE:
415 			break;
416 	}
417 
418 	const skinny_codec_t empty[SKINNY_MAX_CAPABILITIES]  = { SKINNY_CODEC_NONE };
419 	const skinny_codec_t short1[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONSTANDARD, SKINNY_CODEC_G711_ALAW_64K, SKINNY_CODEC_G711_ALAW_56K, SKINNY_CODEC_G711_ULAW_64K, SKINNY_CODEC_G711_ULAW_56K, SKINNY_CODEC_NONE };
420 	const skinny_codec_t short2[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_G711_ULAW_64K, SKINNY_CODEC_G722_64K, SKINNY_CODEC_G711_ULAW_56K, SKINNY_CODEC_G722_56K,
421 		                                                 SKINNY_CODEC_G711_ALAW_64K, SKINNY_CODEC_G722_48K, SKINNY_CODEC_G711_ALAW_56K, SKINNY_CODEC_NONE };
422 	const skinny_codec_t long1[SKINNY_MAX_CAPABILITIES]  = {
423                 SKINNY_CODEC_G729_A,   SKINNY_CODEC_G729,          SKINNY_CODEC_G728,          SKINNY_CODEC_G723_1,        SKINNY_CODEC_G722_48K,      SKINNY_CODEC_G722_56K,
424                 SKINNY_CODEC_G722_64K, SKINNY_CODEC_G711_ULAW_56K, SKINNY_CODEC_G711_ULAW_64K, SKINNY_CODEC_G711_ALAW_56K, SKINNY_CODEC_G711_ALAW_64K, SKINNY_CODEC_IS11172,
425                 SKINNY_CODEC_IS13818,  SKINNY_CODEC_G729_B,        SKINNY_CODEC_G729_AB,       SKINNY_CODEC_GSM_FULLRATE,  SKINNY_CODEC_GSM_HALFRATE,  SKINNY_CODEC_WIDEBAND_256K
426 	};
427 	pbx_test_status_update(test, "Executing reduceCodecSet on two default codecArrays...\n");
428 	{
429 		uint8_t        x                                       = 0;
430 		skinny_codec_t baseCodecArray[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONE };
431 		sccp_codec_reduceSet(baseCodecArray, empty);
432 		for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
433 			pbx_test_validate(test, baseCodecArray[x] == SKINNY_CODEC_NONE);
434 		}
435 	}
436 	pbx_test_status_update(test, "Executing reduceCodecSet on one partially filled and one empty codecArray...\n");
437 	{
438 		uint8_t        x = 0;
439 		skinny_codec_t baseCodecArray[SKINNY_MAX_CAPABILITIES];
440 		memcpy(baseCodecArray, short1, sizeof(skinny_codec_t) * SKINNY_MAX_CAPABILITIES);
441 		sccp_codec_reduceSet(baseCodecArray, empty);
442 		for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
443 			pbx_test_validate(test, baseCodecArray[x] == SKINNY_CODEC_NONE);
444 		}
445 	}
446 	pbx_test_status_update(test, "Executing reduceCodecSet on two partially filled codecArrays...\n");
447 	{
448 		uint8_t        x = 0;
449 		skinny_codec_t baseCodecArray[SKINNY_MAX_CAPABILITIES];
450 		memcpy(baseCodecArray, short1, sizeof(skinny_codec_t) * SKINNY_MAX_CAPABILITIES);
451 		const skinny_codec_t result[SKINNY_MAX_CAPABILITIES] = {
452 			SKINNY_CODEC_G711_ALAW_64K, SKINNY_CODEC_G711_ALAW_56K, SKINNY_CODEC_G711_ULAW_64K, SKINNY_CODEC_G711_ULAW_56K, SKINNY_CODEC_NONE,
453 		};
454 		sccp_codec_reduceSet(baseCodecArray, short2);
455 		for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
456 			// pbx_test_status_update(test, "entry:%d = %s == %s\n", x, codec2str(baseCodecArray[x]), codec2str(result[x]));
457 			pbx_test_validate(test, baseCodecArray[x] == result[x]);
458 		}
459 	}
460 	pbx_test_status_update(test, "Executing reduceCodecSet one fully filled and one partially filled codecArrays...\n");
461 	{
462 		uint8_t        x = 0;
463 		skinny_codec_t baseCodecArray[SKINNY_MAX_CAPABILITIES];
464 		memcpy(baseCodecArray, long1, sizeof(skinny_codec_t) * SKINNY_MAX_CAPABILITIES);
465 		const skinny_codec_t result[SKINNY_MAX_CAPABILITIES] = {
466 			SKINNY_CODEC_G722_48K, SKINNY_CODEC_G722_56K, SKINNY_CODEC_G722_64K, SKINNY_CODEC_G711_ULAW_56K, SKINNY_CODEC_G711_ULAW_64K, SKINNY_CODEC_G711_ALAW_56K, SKINNY_CODEC_G711_ALAW_64K, SKINNY_CODEC_NONE,
467 		};
468 		sccp_codec_reduceSet(baseCodecArray, short2);
469 		for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
470 			pbx_test_status_update(test, "entry:%d = %s == %s\n", x, codec2str(baseCodecArray[x]), codec2str(result[x]));
471 		}
472 		for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
473 			pbx_test_validate(test, baseCodecArray[x] == result[x]);
474 		}
475 	}
476 	return AST_TEST_PASS;
477 }
478 
AST_TEST_DEFINE(chan_sccp_combine_codec_sets)479 AST_TEST_DEFINE(chan_sccp_combine_codec_sets)
480 {
481 	switch (cmd) {
482 		case TEST_INIT:
483 			info->name        = "combineCodecSets";
484 			info->category    = "/channels/chan_sccp/codec/";
485 			info->summary     = "combineCodecSets unit test";
486 			info->description = "combineCodecSets";
487 			return AST_TEST_NOT_RUN;
488 		case TEST_EXECUTE:
489 			break;
490 	}
491 
492 	const skinny_codec_t empty[SKINNY_MAX_CAPABILITIES]  = { SKINNY_CODEC_NONE };
493 	const skinny_codec_t short1[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONSTANDARD, SKINNY_CODEC_G711_ALAW_64K, SKINNY_CODEC_G711_ALAW_56K, SKINNY_CODEC_G711_ULAW_64K, SKINNY_CODEC_G711_ULAW_56K, SKINNY_CODEC_NONE };
494 	const skinny_codec_t short2[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_G711_ULAW_64K, SKINNY_CODEC_G722_64K, SKINNY_CODEC_G711_ULAW_56K, SKINNY_CODEC_G722_56K,
495 		                                                 SKINNY_CODEC_G711_ALAW_64K, SKINNY_CODEC_G722_48K, SKINNY_CODEC_G711_ALAW_56K, SKINNY_CODEC_NONE };
496 	const skinny_codec_t long1[SKINNY_MAX_CAPABILITIES]  = {
497                 SKINNY_CODEC_G729_A,   SKINNY_CODEC_G729,          SKINNY_CODEC_G728,          SKINNY_CODEC_G723_1,        SKINNY_CODEC_G722_48K,      SKINNY_CODEC_G722_56K,
498                 SKINNY_CODEC_G722_64K, SKINNY_CODEC_G711_ULAW_56K, SKINNY_CODEC_G711_ULAW_64K, SKINNY_CODEC_G711_ALAW_56K, SKINNY_CODEC_G711_ALAW_64K, SKINNY_CODEC_IS11172,
499                 SKINNY_CODEC_IS13818,  SKINNY_CODEC_G729_B,        SKINNY_CODEC_G729_AB,       SKINNY_CODEC_GSM_FULLRATE,  SKINNY_CODEC_GSM_HALFRATE,  SKINNY_CODEC_WIDEBAND_256K
500 	};
501 	pbx_test_status_update(test, "Executing combineCodecSet on two empty codecArrays...\n");
502 	{
503 		uint8_t        x                                       = 0;
504 		skinny_codec_t baseCodecArray[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONE };
505 		sccp_codec_combineSets(baseCodecArray, empty);
506 		for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
507 			pbx_test_validate(test, baseCodecArray[x] == SKINNY_CODEC_NONE);
508 		}
509 	}
510 	pbx_test_status_update(test, "Executing combineCodecSet on one partially filled and one empty codecArray...\n");
511 	{
512 		uint8_t        x                                       = 0;
513 		skinny_codec_t baseCodecArray[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONE };
514 		memcpy(baseCodecArray, short1, sizeof(skinny_codec_t) * SKINNY_MAX_CAPABILITIES);
515 		sccp_codec_combineSets(baseCodecArray, empty);
516 		for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
517 			// pbx_test_status_update(test, "entry:%d = %s == %s\n", x, codec2str(baseCodecArray[x]), codec2str(short1[x]));
518 			pbx_test_validate(test, baseCodecArray[x] == short1[x]);
519 		}
520 	}
521 	pbx_test_status_update(test, "Executing combineCodecSet on two partially filled codecArrays...\n");
522 	{
523 		uint8_t        x                                       = 0;
524 		skinny_codec_t baseCodecArray[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONE };
525 		memcpy(baseCodecArray, short1, sizeof(skinny_codec_t) * SKINNY_MAX_CAPABILITIES);
526 		const skinny_codec_t result[SKINNY_MAX_CAPABILITIES] = {
527 			SKINNY_CODEC_NONSTANDARD, SKINNY_CODEC_G711_ALAW_64K, SKINNY_CODEC_G711_ALAW_56K, SKINNY_CODEC_G711_ULAW_64K, SKINNY_CODEC_G711_ULAW_56K,
528 			SKINNY_CODEC_G722_64K,    SKINNY_CODEC_G722_56K,      SKINNY_CODEC_G722_48K,      SKINNY_CODEC_NONE,
529 		};
530 		sccp_codec_combineSets(baseCodecArray, short2);
531 		for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
532 			// pbx_test_status_update(test, "entry:%d = %s == %s\n", x, codec2str(baseCodecArray[x]), codec2str(result[x]));
533 			pbx_test_validate(test, baseCodecArray[x] == result[x]);
534 		}
535 	}
536 	pbx_test_status_update(test, "Executing combineCodecSet on one fully and one partially filled codecArray...\n");
537 	{
538 		uint8_t        x                                       = 0;
539 		skinny_codec_t baseCodecArray[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONE };
540 		memcpy(baseCodecArray, long1, sizeof(skinny_codec_t) * SKINNY_MAX_CAPABILITIES);
541 		sccp_codec_combineSets(baseCodecArray, short2);
542 		for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
543 			// pbx_test_status_update(test, "entry:%d = %s == %s\n", x, codec2str(baseCodecArray[x]), codec2str(long1[x]));
544 			pbx_test_validate(test, baseCodecArray[x] == long1[x]);
545 		}
546 	}
547 	pbx_test_status_update(test, "Executing combineCodecSet on one partially and one fully filled codecArray...\n");
548 	{
549 		uint8_t        x                                       = 0;
550 		skinny_codec_t baseCodecArray[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONE };
551 		memcpy(baseCodecArray, short1, sizeof(skinny_codec_t) * SKINNY_MAX_CAPABILITIES);
552 		const skinny_codec_t result[SKINNY_MAX_CAPABILITIES] = { SKINNY_CODEC_NONSTANDARD, SKINNY_CODEC_G711_ALAW_64K, SKINNY_CODEC_G711_ALAW_56K, SKINNY_CODEC_G711_ULAW_64K, SKINNY_CODEC_G711_ULAW_56K,
553 			                                                 SKINNY_CODEC_G729_A,      SKINNY_CODEC_G729,          SKINNY_CODEC_G728,          SKINNY_CODEC_G723_1,        SKINNY_CODEC_G722_48K,
554 			                                                 SKINNY_CODEC_G722_56K,    SKINNY_CODEC_G722_64K,      SKINNY_CODEC_IS11172,       SKINNY_CODEC_IS13818,       SKINNY_CODEC_G729_B,
555 			                                                 SKINNY_CODEC_G729_AB,     SKINNY_CODEC_GSM_FULLRATE,  SKINNY_CODEC_GSM_HALFRATE };
556 		sccp_codec_combineSets(baseCodecArray, long1);
557 		for (x = 0; x < SKINNY_MAX_CAPABILITIES; x++) {
558 			// pbx_test_status_update(test, "entry:%d = %s == %s\n", x, codec2str(baseCodecArray[x]), codec2str(result[x]));
559 			pbx_test_validate(test, baseCodecArray[x] == result[x]);
560 		}
561 	}
562 	return AST_TEST_PASS;
563 }
564 
sccp_register_tests(void)565 static void __attribute__((constructor)) sccp_register_tests(void)
566 {
567 	AST_TEST_REGISTER(chan_sccp_reduce_codec_set);
568 	AST_TEST_REGISTER(chan_sccp_combine_codec_sets);
569 }
570 
sccp_unregister_tests(void)571 static void __attribute__((destructor)) sccp_unregister_tests(void)
572 {
573 	AST_TEST_UNREGISTER(chan_sccp_reduce_codec_set);
574 	AST_TEST_UNREGISTER(chan_sccp_combine_codec_sets);
575 }
576 #endif
577 // kate: indent-width 8; replace-tabs off; indent-mode cstyle; auto-insert-doxygen on; line-numbers on; tab-indents on; keep-extra-spaces off; auto-brackets off;
578