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