/src/janus-gateway/src/utils.c
Line | Count | Source |
1 | | /*! \file utils.c |
2 | | * \author Lorenzo Miniero <lorenzo@meetecho.com> |
3 | | * \copyright GNU General Public License v3 |
4 | | * \brief Utilities and helpers |
5 | | * \details Implementations of a few methods that may be of use here |
6 | | * and there in the code. |
7 | | * |
8 | | * \ingroup core |
9 | | * \ref core |
10 | | */ |
11 | | |
12 | | #include <stdlib.h> |
13 | | #include <string.h> |
14 | | #include <sys/stat.h> |
15 | | #include <errno.h> |
16 | | #include <fcntl.h> |
17 | | #include <sys/file.h> |
18 | | #include <sys/types.h> |
19 | | #include <unistd.h> |
20 | | #include <arpa/inet.h> |
21 | | #include <inttypes.h> |
22 | | |
23 | | #include <zlib.h> |
24 | | #include <openssl/rand.h> |
25 | | |
26 | | #include "utils.h" |
27 | | #include "rtp.h" |
28 | | #include "debug.h" |
29 | | #include "mutex.h" |
30 | | |
31 | | #if __MACH__ |
32 | | #include "mach_gettime.h" |
33 | | #endif |
34 | | |
35 | 3.75k | gint64 janus_get_monotonic_time_internal(void) { |
36 | 3.75k | struct timespec ts; |
37 | 3.75k | clock_gettime (CLOCK_MONOTONIC, &ts); |
38 | 3.75k | return (ts.tv_sec*G_GINT64_CONSTANT(1000000)) + (ts.tv_nsec/G_GINT64_CONSTANT(1000)); |
39 | 3.75k | } |
40 | | |
41 | | static gint64 janus_started = 0; |
42 | 0 | void janus_mark_started(void) { |
43 | 0 | if(janus_started == 0) |
44 | 0 | janus_started = janus_get_monotonic_time_internal(); |
45 | 0 | } |
46 | | |
47 | 3.75k | gint64 janus_get_monotonic_time(void) { |
48 | 3.75k | return janus_get_monotonic_time_internal() - janus_started; |
49 | 3.75k | } |
50 | | |
51 | 0 | gint64 janus_get_real_time(void) { |
52 | 0 | struct timespec ts; |
53 | 0 | clock_gettime (CLOCK_REALTIME, &ts); |
54 | 0 | return (ts.tv_sec*G_GINT64_CONSTANT(1000000)) + (ts.tv_nsec/G_GINT64_CONSTANT(1000)); |
55 | 0 | } |
56 | | |
57 | 0 | gboolean janus_is_true(const char *value) { |
58 | 0 | return value && (!strcasecmp(value, "yes") || !strcasecmp(value, "true") || !strcasecmp(value, "1")); |
59 | 0 | } |
60 | | |
61 | 0 | gboolean janus_strcmp_const_time(const void *str1, const void *str2) { |
62 | 0 | if(str1 == NULL || str2 == NULL) |
63 | 0 | return FALSE; |
64 | 0 | const unsigned char *string1 = (const unsigned char *)str1; |
65 | 0 | const unsigned char *string2 = (const unsigned char *)str2; |
66 | 0 | size_t maxlen = strlen((char *)string1); |
67 | 0 | if(strlen((char *)string2) > maxlen) |
68 | 0 | maxlen = strlen((char *)string2); |
69 | 0 | unsigned char *buf1 = g_malloc0(maxlen+1); |
70 | 0 | memcpy(buf1, string1, strlen(str1)); |
71 | 0 | unsigned char *buf2 = g_malloc0(maxlen+1); |
72 | 0 | memcpy(buf2, string2, strlen(str2)); |
73 | 0 | unsigned char result = 0; |
74 | 0 | size_t i = 0; |
75 | 0 | for (i = 0; i < maxlen; i++) { |
76 | 0 | result |= buf1[i] ^ buf2[i]; |
77 | 0 | } |
78 | 0 | g_free(buf1); |
79 | 0 | buf1 = NULL; |
80 | 0 | g_free(buf2); |
81 | 0 | buf2 = NULL; |
82 | 0 | return result == 0; |
83 | 0 | } |
84 | | |
85 | 0 | guint32 janus_random_uint32(void) { |
86 | 0 | guint32 ret = 0; |
87 | 0 | if(RAND_bytes((void *)&ret, sizeof(ret)) != 1) { |
88 | 0 | JANUS_LOG(LOG_WARN, "Safe RAND_bytes() failed, falling back to unsafe PRNG\n"); |
89 | 0 | return g_random_int(); |
90 | 0 | } |
91 | 0 | return ret; |
92 | 0 | } |
93 | | |
94 | 0 | guint64 janus_random_uint64_full(void) { |
95 | 0 | guint64 ret = 0; |
96 | 0 | if(RAND_bytes((void *)&ret, sizeof(ret)) != 1) { |
97 | 0 | JANUS_LOG(LOG_WARN, "Safe RAND_bytes() failed, falling back to unsafe PRNG\n"); |
98 | 0 | return ((guint64)g_random_int() << 32) | g_random_int(); |
99 | 0 | } |
100 | 0 | return ret; |
101 | 0 | } |
102 | | |
103 | 0 | guint64 janus_random_uint64(void) { |
104 | 0 | return janus_random_uint64_full() & 0x1FFFFFFFFFFFFF; |
105 | 0 | } |
106 | | |
107 | 0 | char *janus_random_uuid(void) { |
108 | 0 | #if GLIB_CHECK_VERSION(2, 52, 0) |
109 | 0 | return g_uuid_string_random(); |
110 | | #else |
111 | | /* g_uuid_string_random is only available from glib 2.52, so if it's |
112 | | * not available we have to do it manually: the following code is |
113 | | * heavily based on https://github.com/rxi/uuid4 (MIT license) */ |
114 | | const char *template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"; |
115 | | const char *samples = "0123456789abcdef"; |
116 | | union { unsigned char b[16]; uint64_t word[2]; } rnd; |
117 | | rnd.word[0] = janus_random_uint64_full(); |
118 | | rnd.word[1] = janus_random_uint64_full(); |
119 | | /* Generate the string */ |
120 | | char uuid[37], *dst = uuid; |
121 | | const char *p = template; |
122 | | int i = 0, n = 0; |
123 | | while(*p) { |
124 | | n = rnd.b[i >> 1]; |
125 | | n = (i & 1) ? (n >> 4) : (n & 0xf); |
126 | | switch (*p) { |
127 | | case 'x': |
128 | | *dst = samples[n]; |
129 | | i++; |
130 | | break; |
131 | | case 'y': |
132 | | *dst = samples[(n & 0x3) + 8]; |
133 | | i++; |
134 | | break; |
135 | | default: |
136 | | *dst = *p; |
137 | | } |
138 | | p++; |
139 | | dst++; |
140 | | } |
141 | | uuid[36] = '\0'; |
142 | | return g_strdup(uuid); |
143 | | #endif |
144 | 0 | } |
145 | | |
146 | 0 | guint64 *janus_uint64_dup(guint64 num) { |
147 | 0 | guint64 *numdup = g_malloc(sizeof(guint64)); |
148 | 0 | *numdup = num; |
149 | 0 | return numdup; |
150 | 0 | } |
151 | | |
152 | 0 | guint64 janus_uint64_hash(guint64 num) { |
153 | 0 | num = (num ^ (num >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); |
154 | 0 | num = (num ^ (num >> 27)) * UINT64_C(0x94d049bb133111eb); |
155 | 0 | num = num ^ (num >> 31); |
156 | 0 | return num; |
157 | 0 | } |
158 | | |
159 | 0 | int janus_string_to_uint8(const char *str, uint8_t *num) { |
160 | 0 | if(str == NULL || num == NULL) |
161 | 0 | return -EINVAL; |
162 | 0 | long int val = strtol(str, 0, 10); |
163 | 0 | if(val < 0 || val > UINT8_MAX) |
164 | 0 | return -ERANGE; |
165 | 0 | *num = val; |
166 | 0 | return 0; |
167 | 0 | } |
168 | | |
169 | 0 | int janus_string_to_uint16(const char *str, uint16_t *num) { |
170 | 0 | if(str == NULL || num == NULL) |
171 | 0 | return -EINVAL; |
172 | 0 | long int val = strtol(str, 0, 10); |
173 | 0 | if(val < 0 || val > UINT16_MAX) |
174 | 0 | return -ERANGE; |
175 | 0 | *num = val; |
176 | 0 | return 0; |
177 | 0 | } |
178 | | |
179 | 0 | int janus_string_to_uint32(const char *str, uint32_t *num) { |
180 | 0 | if(str == NULL || num == NULL) |
181 | 0 | return -EINVAL; |
182 | 0 | long long int val = strtoll(str, 0, 10); |
183 | 0 | if(val < 0 || val > UINT32_MAX) |
184 | 0 | return -ERANGE; |
185 | 0 | *num = val; |
186 | 0 | return 0; |
187 | 0 | } |
188 | | |
189 | 0 | void janus_flags_reset(janus_flags *flags) { |
190 | 0 | if(flags != NULL) |
191 | 0 | g_atomic_pointer_set(flags, 0); |
192 | 0 | } |
193 | | |
194 | 0 | void janus_flags_set(janus_flags *flags, gsize flag) { |
195 | 0 | if(flags != NULL) { |
196 | 0 | g_atomic_pointer_or(flags, flag); |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | 0 | void janus_flags_clear(janus_flags *flags, gsize flag) { |
201 | 0 | if(flags != NULL) { |
202 | 0 | g_atomic_pointer_and(flags, ~(flag)); |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | 0 | gboolean janus_flags_is_set(janus_flags *flags, gsize flag) { |
207 | 0 | if(flags != NULL) { |
208 | 0 | gsize bit = ((gsize) g_atomic_pointer_get(flags)) & flag; |
209 | 0 | return (bit != 0); |
210 | 0 | } |
211 | 0 | return FALSE; |
212 | 0 | } |
213 | | |
214 | | /* Easy way to replace multiple occurrences of a string with another */ |
215 | | char *janus_string_replace(char *message, const char *old_string, const char *new_string) |
216 | 0 | { |
217 | 0 | if(!message || !old_string || !new_string) |
218 | 0 | return NULL; |
219 | | |
220 | 0 | if(!strstr(message, old_string)) { /* Nothing to be done (old_string is not there) */ |
221 | 0 | return message; |
222 | 0 | } |
223 | 0 | if(!strcmp(old_string, new_string)) { /* Nothing to be done (old_string=new_string) */ |
224 | 0 | return message; |
225 | 0 | } |
226 | 0 | if(strlen(old_string) == strlen(new_string)) { /* Just overwrite */ |
227 | 0 | char *outgoing = message; |
228 | 0 | char *pos = strstr(outgoing, old_string), *tmp = NULL; |
229 | 0 | while(pos) { |
230 | 0 | memcpy(pos, new_string, strlen(new_string)); |
231 | 0 | pos += strlen(old_string); |
232 | 0 | tmp = strstr(pos, old_string); |
233 | 0 | pos = tmp; |
234 | 0 | } |
235 | 0 | return outgoing; |
236 | 0 | } else { /* We need to resize */ |
237 | 0 | char *outgoing = g_strdup(message); |
238 | 0 | g_free(message); |
239 | 0 | if(outgoing == NULL) { |
240 | 0 | return NULL; |
241 | 0 | } |
242 | 0 | int diff = strlen(new_string) - strlen(old_string); |
243 | | /* Count occurrences */ |
244 | 0 | int counter = 0; |
245 | 0 | char *pos = strstr(outgoing, old_string), *tmp = NULL; |
246 | 0 | while(pos) { |
247 | 0 | counter++; |
248 | 0 | pos += strlen(old_string); |
249 | 0 | tmp = strstr(pos, old_string); |
250 | 0 | pos = tmp; |
251 | 0 | } |
252 | 0 | uint16_t old_stringlen = strlen(outgoing)+1, new_stringlen = old_stringlen + diff*counter; |
253 | 0 | if(diff > 0) { /* Resize now */ |
254 | 0 | tmp = g_realloc(outgoing, new_stringlen); |
255 | 0 | outgoing = tmp; |
256 | 0 | } |
257 | | /* Replace string */ |
258 | 0 | pos = strstr(outgoing, old_string); |
259 | 0 | while(pos) { |
260 | 0 | if(diff > 0) { /* Move to the right (new_string is larger than old_string) */ |
261 | 0 | uint16_t len = strlen(pos)+1; |
262 | 0 | memmove(pos + diff, pos, len); |
263 | 0 | memcpy(pos, new_string, strlen(new_string)); |
264 | 0 | pos += strlen(new_string); |
265 | 0 | tmp = strstr(pos, old_string); |
266 | 0 | } else { /* Move to the left (new_string is smaller than old_string) */ |
267 | 0 | uint16_t len = strlen(pos - diff)+1; |
268 | 0 | memmove(pos, pos - diff, len); |
269 | 0 | memcpy(pos, new_string, strlen(new_string)); |
270 | 0 | pos += strlen(old_string); |
271 | 0 | tmp = strstr(pos, old_string); |
272 | 0 | } |
273 | 0 | pos = tmp; |
274 | 0 | } |
275 | 0 | if(diff < 0) { /* We skipped the resize previously (shrinking memory) */ |
276 | 0 | tmp = g_realloc(outgoing, new_stringlen); |
277 | 0 | outgoing = tmp; |
278 | 0 | } |
279 | 0 | outgoing[strlen(outgoing)] = '\0'; |
280 | 0 | return outgoing; |
281 | 0 | } |
282 | 0 | } |
283 | | |
284 | 0 | size_t janus_strlcat(char *dest, const char *src, size_t dest_size) { |
285 | 0 | size_t ret = g_strlcat(dest, src, dest_size); |
286 | 0 | if(ret >= dest_size) |
287 | 0 | JANUS_LOG(LOG_ERR, "Truncation occurred, %lu >= %lu\n", ret, dest_size); |
288 | 0 | return ret; |
289 | 0 | } |
290 | | |
291 | 1.57M | int janus_strlcat_fast(char *dest, const char *src, size_t dest_size, size_t *offset) { |
292 | 1.57M | if(dest == NULL || src == NULL || offset == NULL) { |
293 | 0 | JANUS_LOG(LOG_ERR, "Invalid arguments\n"); |
294 | 0 | return -1; |
295 | 0 | } |
296 | 1.57M | if(*offset >= dest_size) { |
297 | 3.30k | JANUS_LOG(LOG_ERR, "Offset is beyond the buffer size\n"); |
298 | 3.30k | return -2; |
299 | 3.30k | } |
300 | 1.56M | char *p = memccpy(dest + *offset, src, 0, dest_size - *offset); |
301 | 1.56M | if(p == NULL) { |
302 | 203 | JANUS_LOG(LOG_ERR, "Truncation occurred, %lu >= %lu\n", |
303 | 203 | *offset + strlen(src), dest_size); |
304 | 203 | *offset = dest_size; |
305 | 203 | *(dest + dest_size -1) = '\0'; |
306 | 203 | return -3; |
307 | 203 | } |
308 | 1.56M | *offset = (p - dest - 1); |
309 | 1.56M | return 0; |
310 | 1.56M | } |
311 | | |
312 | 0 | int janus_mkdir(const char *dir, mode_t mode) { |
313 | 0 | char tmp[256]; |
314 | 0 | char *p = NULL; |
315 | 0 | size_t len; |
316 | |
|
317 | 0 | int res = 0; |
318 | 0 | g_snprintf(tmp, sizeof(tmp), "%s", dir); |
319 | 0 | len = strlen(tmp); |
320 | 0 | if(tmp[len - 1] == '/') |
321 | 0 | tmp[len - 1] = 0; |
322 | 0 | for(p = tmp + 1; *p; p++) { |
323 | 0 | if(*p == '/') { |
324 | 0 | *p = 0; |
325 | 0 | res = mkdir(tmp, mode); |
326 | 0 | if(res != 0 && errno != EEXIST) { |
327 | 0 | JANUS_LOG(LOG_ERR, "Error creating folder %s\n", tmp); |
328 | 0 | return res; |
329 | 0 | } |
330 | 0 | *p = '/'; |
331 | 0 | } |
332 | 0 | } |
333 | 0 | res = mkdir(tmp, mode); |
334 | 0 | if(res != 0 && errno != EEXIST) |
335 | 0 | return res; |
336 | 0 | return 0; |
337 | 0 | } |
338 | | |
339 | 0 | gchar *janus_make_absolute_path(const gchar *base_dir, const gchar *path) { |
340 | 0 | if(!path) |
341 | 0 | return NULL; |
342 | 0 | if(g_path_is_absolute(path)) |
343 | 0 | return g_strdup(path); |
344 | 0 | if(!base_dir) |
345 | 0 | return NULL; |
346 | 0 | return g_build_filename(base_dir, path, NULL); |
347 | 0 | } |
348 | | |
349 | 0 | int janus_get_codec_pt(const char *sdp, const char *codec) { |
350 | 0 | if(!sdp || !codec) |
351 | 0 | return -1; |
352 | 0 | int video = 0; |
353 | 0 | const char *format = NULL, *format2 = NULL; |
354 | 0 | if(!strcasecmp(codec, "opus")) { |
355 | 0 | video = 0; |
356 | 0 | format = "opus/48000/2"; |
357 | 0 | format2 = "OPUS/48000/2"; |
358 | 0 | } else if(!strcasecmp(codec, "pcmu")) { |
359 | | /* We know the payload type is 0: we just need to make sure it's there */ |
360 | 0 | video = 0; |
361 | 0 | format = "pcmu/8000"; |
362 | 0 | format2 = "PCMU/8000"; |
363 | 0 | } else if(!strcasecmp(codec, "pcma")) { |
364 | | /* We know the payload type is 8: we just need to make sure it's there */ |
365 | 0 | video = 0; |
366 | 0 | format = "pcma/8000"; |
367 | 0 | format2 = "PCMA/8000"; |
368 | 0 | } else if(!strcasecmp(codec, "g722")) { |
369 | | /* We know the payload type is 9: we just need to make sure it's there */ |
370 | 0 | video = 0; |
371 | 0 | format = "g722/8000"; |
372 | 0 | format2 = "G722/8000"; |
373 | 0 | } else if(!strcasecmp(codec, "isac16")) { |
374 | 0 | video = 0; |
375 | 0 | format = "isac/16000"; |
376 | 0 | format2 = "ISAC/16000"; |
377 | 0 | } else if(!strcasecmp(codec, "isac32")) { |
378 | 0 | video = 0; |
379 | 0 | format = "isac/32000"; |
380 | 0 | format2 = "ISAC/32000"; |
381 | 0 | } else if(!strcasecmp(codec, "l16-48")) { |
382 | 0 | video = 0; |
383 | 0 | format = "l16/48000"; |
384 | 0 | format2 = "L16/48000"; |
385 | 0 | } else if(!strcasecmp(codec, "l16")) { |
386 | 0 | video = 0; |
387 | 0 | format = "l16/16000"; |
388 | 0 | format2 = "L16/16000"; |
389 | 0 | } else if(!strcasecmp(codec, "vp8")) { |
390 | 0 | video = 1; |
391 | 0 | format = "vp8/90000"; |
392 | 0 | format2 = "VP8/90000"; |
393 | 0 | } else if(!strcasecmp(codec, "vp9")) { |
394 | 0 | video = 1; |
395 | 0 | format = "vp9/90000"; |
396 | 0 | format2 = "VP9/90000"; |
397 | 0 | } else if(!strcasecmp(codec, "h264")) { |
398 | 0 | video = 1; |
399 | 0 | format = "h264/90000"; |
400 | 0 | format2 = "H264/90000"; |
401 | 0 | } else if(!strcasecmp(codec, "av1")) { |
402 | 0 | video = 1; |
403 | 0 | format = "av1/90000"; |
404 | 0 | format2 = "AV1/90000"; |
405 | 0 | } else if(!strcasecmp(codec, "h265")) { |
406 | 0 | video = 1; |
407 | 0 | format = "h265/90000"; |
408 | 0 | format2 = "H265/90000"; |
409 | 0 | } else { |
410 | 0 | JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", codec); |
411 | 0 | return -1; |
412 | 0 | } |
413 | | /* First of all, let's check if the codec is there */ |
414 | 0 | if(!video) { |
415 | 0 | if(!strstr(sdp, "m=audio") || (!strstr(sdp, format) && !strstr(sdp, format2))) |
416 | 0 | return -2; |
417 | 0 | } else { |
418 | 0 | if(!strstr(sdp, "m=video") || (!strstr(sdp, format) && !strstr(sdp, format2))) |
419 | 0 | return -2; |
420 | 0 | } |
421 | 0 | char rtpmap[50], rtpmap2[50]; |
422 | 0 | g_snprintf(rtpmap, 50, "a=rtpmap:%%d %s", format); |
423 | 0 | g_snprintf(rtpmap2, 50, "a=rtpmap:%%d %s", format2); |
424 | | /* Look for the mapping */ |
425 | 0 | const char *line = strstr(sdp, video ? "m=video" : "m=audio"); |
426 | 0 | while(line) { |
427 | 0 | char *next = strchr(line, '\n'); |
428 | 0 | if(next) { |
429 | 0 | *next = '\0'; |
430 | 0 | if(strstr(line, "a=rtpmap") && strstr(line, format)) { |
431 | | /* Gotcha! */ |
432 | 0 | int pt = 0; |
433 | 0 | #pragma GCC diagnostic push |
434 | 0 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" |
435 | 0 | if(sscanf(line, rtpmap, &pt) == 1) { |
436 | 0 | *next = '\n'; |
437 | 0 | return pt; |
438 | 0 | } |
439 | 0 | } else if(strstr(line, "a=rtpmap") && strstr(line, format2)) { |
440 | | /* Gotcha! */ |
441 | 0 | int pt = 0; |
442 | 0 | if(sscanf(line, rtpmap2, &pt) == 1) { |
443 | 0 | #pragma GCC diagnostic pop |
444 | 0 | *next = '\n'; |
445 | 0 | return pt; |
446 | 0 | } |
447 | 0 | } |
448 | 0 | *next = '\n'; |
449 | 0 | } |
450 | 0 | line = next ? (next+1) : NULL; |
451 | 0 | } |
452 | 0 | return -3; |
453 | 0 | } |
454 | | |
455 | 0 | const char *janus_get_codec_from_pt(const char *sdp, int pt) { |
456 | 0 | if(!sdp || pt < 0) |
457 | 0 | return NULL; |
458 | 0 | if(pt == 0) |
459 | 0 | return "pcmu"; |
460 | 0 | if(pt == 8) |
461 | 0 | return "pcma"; |
462 | 0 | if(pt == 9) |
463 | 0 | return "g722"; |
464 | | /* Look for the mapping */ |
465 | 0 | char rtpmap[50]; |
466 | 0 | g_snprintf(rtpmap, 50, "a=rtpmap:%d ", pt); |
467 | 0 | const char *line = strstr(sdp, "m="); |
468 | 0 | while(line) { |
469 | 0 | char *next = strchr(line, '\n'); |
470 | 0 | if(next) { |
471 | 0 | *next = '\0'; |
472 | 0 | if(strstr(line, rtpmap)) { |
473 | | /* Gotcha! */ |
474 | 0 | char name[100]; |
475 | 0 | if(sscanf(line, "a=rtpmap:%d %99s", &pt, name) == 2) { |
476 | 0 | *next = '\n'; |
477 | 0 | if(strstr(name, "vp8") || strstr(name, "VP8")) |
478 | 0 | return "vp8"; |
479 | 0 | if(strstr(name, "vp9") || strstr(name, "VP9")) |
480 | 0 | return "vp9"; |
481 | 0 | if(strstr(name, "h264") || strstr(name, "H264")) |
482 | 0 | return "h264"; |
483 | 0 | if(strstr(name, "av1") || strstr(name, "AV1")) |
484 | 0 | return "av1"; |
485 | 0 | if(strstr(name, "h265") || strstr(name, "H265")) |
486 | 0 | return "h265"; |
487 | 0 | if(strstr(name, "opus") || strstr(name, "OPUS")) |
488 | 0 | return "opus"; |
489 | 0 | if(strstr(name, "pcmu") || strstr(name, "PCMU")) |
490 | 0 | return "pcmu"; |
491 | 0 | if(strstr(name, "pcma") || strstr(name, "PCMA")) |
492 | 0 | return "pcma"; |
493 | 0 | if(strstr(name, "g722") || strstr(name, "G722")) |
494 | 0 | return "g722"; |
495 | 0 | if(strstr(name, "isac/16") || strstr(name, "ISAC/16")) |
496 | 0 | return "isac16"; |
497 | 0 | if(strstr(name, "isac/32") || strstr(name, "ISAC/32")) |
498 | 0 | return "isac32"; |
499 | 0 | if(strstr(name, "l16/48") || strstr(name, "L16/48")) |
500 | 0 | return "l16-48"; |
501 | 0 | if(strstr(name, "l16/16") || strstr(name, "L16/16")) |
502 | 0 | return "l16"; |
503 | 0 | if(strstr(name, "red")) |
504 | 0 | return NULL; |
505 | 0 | JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", name); |
506 | 0 | return NULL; |
507 | 0 | } |
508 | 0 | } |
509 | 0 | *next = '\n'; |
510 | 0 | } |
511 | 0 | line = next ? (next+1) : NULL; |
512 | 0 | } |
513 | 0 | return NULL; |
514 | 0 | } |
515 | | |
516 | | /* PID file management */ |
517 | | static char *pidfile = NULL; |
518 | | static int pidfd = -1; |
519 | | static FILE *pidf = NULL; |
520 | 0 | int janus_pidfile_create(const char *file) { |
521 | 0 | if(file == NULL) |
522 | 0 | return 0; |
523 | 0 | pidfile = g_strdup(file); |
524 | | /* Try creating a PID file (or opening an existing one) */ |
525 | 0 | pidfd = open(pidfile, O_RDWR|O_CREAT|O_TRUNC, 0644); |
526 | 0 | if(pidfd < 0) { |
527 | 0 | JANUS_LOG(LOG_FATAL, "Error opening/creating PID file %s, does Janus have enough permissions?\n", pidfile); |
528 | 0 | return -1; |
529 | 0 | } |
530 | 0 | pidf = fdopen(pidfd, "r+"); |
531 | 0 | if(pidf == NULL) { |
532 | 0 | JANUS_LOG(LOG_FATAL, "Error opening/creating PID file %s, does Janus have enough permissions?\n", pidfile); |
533 | 0 | close(pidfd); |
534 | 0 | return -1; |
535 | 0 | } |
536 | | /* Try locking the PID file */ |
537 | 0 | int pid = 0; |
538 | 0 | if(flock(pidfd, LOCK_EX|LOCK_NB) < 0) { |
539 | 0 | if(fscanf(pidf, "%d", &pid) == 1) { |
540 | 0 | JANUS_LOG(LOG_FATAL, "Error locking PID file (lock held by PID %d?)\n", pid); |
541 | 0 | } else { |
542 | 0 | JANUS_LOG(LOG_FATAL, "Error locking PID file (lock held by unknown PID?)\n"); |
543 | 0 | } |
544 | 0 | fclose(pidf); |
545 | 0 | return -1; |
546 | 0 | } |
547 | | /* Write the PID */ |
548 | 0 | pid = getpid(); |
549 | 0 | if(fprintf(pidf, "%d\n", pid) < 0) { |
550 | 0 | JANUS_LOG(LOG_FATAL, "Error writing PID in file, error %d (%s)\n", errno, g_strerror(errno)); |
551 | 0 | fclose(pidf); |
552 | 0 | return -1; |
553 | 0 | } |
554 | 0 | fflush(pidf); |
555 | | /* We're done */ |
556 | 0 | return 0; |
557 | 0 | } |
558 | | |
559 | 0 | int janus_pidfile_remove(void) { |
560 | 0 | if(pidfile == NULL || pidfd < 0 || pidf == NULL) |
561 | 0 | return 0; |
562 | | /* Unlock the PID file and remove it */ |
563 | 0 | if(flock(pidfd, LOCK_UN) < 0) { |
564 | 0 | JANUS_LOG(LOG_FATAL, "Error unlocking PID file\n"); |
565 | 0 | fclose(pidf); |
566 | 0 | close(pidfd); |
567 | 0 | return -1; |
568 | 0 | } |
569 | 0 | fclose(pidf); |
570 | 0 | unlink(pidfile); |
571 | 0 | g_free(pidfile); |
572 | 0 | return 0; |
573 | 0 | } |
574 | | |
575 | | /* Protected folders management */ |
576 | | static GList *protected_folders = NULL; |
577 | | static janus_mutex pf_mutex = JANUS_MUTEX_INITIALIZER; |
578 | | |
579 | 0 | void janus_protected_folder_add(const char *folder) { |
580 | 0 | if(folder == NULL) |
581 | 0 | return; |
582 | 0 | janus_mutex_lock(&pf_mutex); |
583 | 0 | protected_folders = g_list_append(protected_folders, g_strdup(folder)); |
584 | 0 | janus_mutex_unlock(&pf_mutex); |
585 | 0 | } |
586 | | |
587 | 0 | gboolean janus_is_folder_protected(const char *path) { |
588 | | /* We need a valid pathname (can't start with a space, we don't trim) */ |
589 | 0 | if(path == NULL || *path == ' ') |
590 | 0 | return TRUE; |
591 | | /* Resolve the pathname to its real path first */ |
592 | 0 | char resolved[PATH_MAX+1]; |
593 | 0 | resolved[0] = '\0'; |
594 | 0 | if(realpath(path, resolved) == NULL && errno != ENOENT) { |
595 | 0 | JANUS_LOG(LOG_ERR, "Error resolving path '%s'... %d (%s)\n", |
596 | 0 | path, errno, g_strerror(errno)); |
597 | 0 | return TRUE; |
598 | 0 | } |
599 | | /* Traverse the list of protected folders to see if any match */ |
600 | 0 | janus_mutex_lock(&pf_mutex); |
601 | 0 | if(protected_folders == NULL) { |
602 | | /* No protected folder in the list */ |
603 | 0 | janus_mutex_unlock(&pf_mutex); |
604 | 0 | return FALSE; |
605 | 0 | } |
606 | 0 | gboolean protected = FALSE; |
607 | 0 | GList *temp = protected_folders; |
608 | 0 | while(temp) { |
609 | 0 | char *folder = (char *)temp->data; |
610 | 0 | if(folder && (strstr(resolved, folder) == resolved)) { |
611 | 0 | protected = TRUE; |
612 | 0 | break; |
613 | 0 | } |
614 | 0 | temp = temp->next; |
615 | 0 | } |
616 | 0 | janus_mutex_unlock(&pf_mutex); |
617 | 0 | return protected; |
618 | 0 | } |
619 | | |
620 | 0 | void janus_protected_folders_clear(void) { |
621 | 0 | janus_mutex_lock(&pf_mutex); |
622 | 0 | g_list_free_full(protected_folders, (GDestroyNotify)g_free); |
623 | 0 | janus_mutex_unlock(&pf_mutex); |
624 | 0 | } |
625 | | |
626 | | |
627 | 0 | void janus_get_json_type_name(int jtype, unsigned int flags, char *type_name) { |
628 | | /* Longest possible combination is "a non-empty boolean" plus one for null char */ |
629 | 0 | gsize req_size = 20; |
630 | | /* Don't allow for both "positive" and "non-empty" because that needlessly increases the size. */ |
631 | 0 | if((flags & JANUS_JSON_PARAM_POSITIVE) != 0) { |
632 | 0 | g_strlcpy(type_name, "a positive ", req_size); |
633 | 0 | } |
634 | 0 | else if((flags & JANUS_JSON_PARAM_NONEMPTY) != 0) { |
635 | 0 | g_strlcpy(type_name, "a non-empty ", req_size); |
636 | 0 | } |
637 | 0 | else if(jtype == JSON_INTEGER || jtype == JSON_ARRAY || jtype == JSON_OBJECT) { |
638 | 0 | g_strlcpy(type_name, "an ", req_size); |
639 | 0 | } |
640 | 0 | else { |
641 | 0 | g_strlcpy(type_name, "a ", req_size); |
642 | 0 | } |
643 | 0 | switch(jtype) { |
644 | 0 | case JSON_TRUE: |
645 | 0 | janus_strlcat(type_name, "boolean", req_size); |
646 | 0 | break; |
647 | 0 | case JSON_INTEGER: |
648 | 0 | janus_strlcat(type_name, "integer", req_size); |
649 | 0 | break; |
650 | 0 | case JSON_REAL: |
651 | 0 | janus_strlcat(type_name, "real", req_size); |
652 | 0 | break; |
653 | 0 | case JSON_STRING: |
654 | 0 | janus_strlcat(type_name, "string", req_size); |
655 | 0 | break; |
656 | 0 | case JSON_ARRAY: |
657 | 0 | janus_strlcat(type_name, "array", req_size); |
658 | 0 | break; |
659 | 0 | case JSON_OBJECT: |
660 | 0 | janus_strlcat(type_name, "object", req_size); |
661 | 0 | break; |
662 | 0 | default: |
663 | 0 | break; |
664 | 0 | } |
665 | 0 | } |
666 | | |
667 | 0 | gboolean janus_json_is_valid(json_t *val, json_type jtype, unsigned int flags) { |
668 | 0 | gboolean is_valid = (json_typeof(val) == jtype || (jtype == JSON_TRUE && json_typeof(val) == JSON_FALSE)); |
669 | 0 | if(!is_valid) |
670 | 0 | return FALSE; |
671 | 0 | if((flags & JANUS_JSON_PARAM_POSITIVE) != 0) { |
672 | 0 | switch(jtype) { |
673 | 0 | case JSON_INTEGER: |
674 | 0 | is_valid = (json_integer_value(val) >= 0); |
675 | 0 | break; |
676 | 0 | case JSON_REAL: |
677 | 0 | is_valid = (json_real_value(val) >= 0); |
678 | 0 | break; |
679 | 0 | default: |
680 | 0 | break; |
681 | 0 | } |
682 | 0 | } |
683 | 0 | else if((flags & JANUS_JSON_PARAM_NONEMPTY) != 0) { |
684 | 0 | switch(jtype) { |
685 | 0 | case JSON_STRING: |
686 | 0 | is_valid = (strlen(json_string_value(val)) > 0); |
687 | 0 | break; |
688 | 0 | case JSON_ARRAY: |
689 | 0 | is_valid = (json_array_size(val) > 0); |
690 | 0 | break; |
691 | 0 | default: |
692 | 0 | break; |
693 | 0 | } |
694 | 0 | } |
695 | 0 | return is_valid; |
696 | 0 | } |
697 | | |
698 | | /* The following code is more related to codec specific helpers */ |
699 | | #if defined(__ppc__) || defined(__ppc64__) |
700 | | # define swap2(d) \ |
701 | | ((d&0x000000ff)<<8) | \ |
702 | | ((d&0x0000ff00)>>8) |
703 | | #else |
704 | 4 | # define swap2(d) d |
705 | | #endif |
706 | | |
707 | 617 | gboolean janus_vp8_is_keyframe(const char *buffer, int len) { |
708 | 617 | if(!buffer || len < 16) |
709 | 378 | return FALSE; |
710 | | /* Parse VP8 header now */ |
711 | 239 | uint8_t vp8pd = *buffer; |
712 | 239 | uint8_t xbit = (vp8pd & 0x80); |
713 | 239 | uint8_t sbit = (vp8pd & 0x10); |
714 | 239 | if(xbit) { |
715 | 98 | JANUS_LOG(LOG_HUGE, " -- X bit is set!\n"); |
716 | | /* Read the Extended control bits octet */ |
717 | 98 | buffer++; |
718 | 98 | vp8pd = *buffer; |
719 | 98 | uint8_t ibit = (vp8pd & 0x80); |
720 | 98 | uint8_t lbit = (vp8pd & 0x40); |
721 | 98 | uint8_t tbit = (vp8pd & 0x20); |
722 | 98 | uint8_t kbit = (vp8pd & 0x10); |
723 | 98 | if(ibit) { |
724 | 67 | JANUS_LOG(LOG_HUGE, " -- I bit is set!\n"); |
725 | | /* Read the PictureID octet */ |
726 | 67 | buffer++; |
727 | 67 | vp8pd = *buffer; |
728 | 67 | uint16_t picid = vp8pd, wholepicid = picid; |
729 | 67 | uint8_t mbit = (vp8pd & 0x80); |
730 | 67 | if(mbit) { |
731 | 49 | JANUS_LOG(LOG_HUGE, " -- M bit is set!\n"); |
732 | 49 | memcpy(&picid, buffer, sizeof(uint16_t)); |
733 | 49 | wholepicid = ntohs(picid); |
734 | 49 | picid = (wholepicid & 0x7FFF); |
735 | 49 | buffer++; |
736 | 49 | } |
737 | 67 | JANUS_LOG(LOG_HUGE, " -- -- PictureID: %"SCNu16"\n", picid); |
738 | 67 | } |
739 | 98 | if(lbit) { |
740 | 48 | JANUS_LOG(LOG_HUGE, " -- L bit is set!\n"); |
741 | | /* Read the TL0PICIDX octet */ |
742 | 48 | buffer++; |
743 | 48 | vp8pd = *buffer; |
744 | 48 | } |
745 | 98 | if(tbit || kbit) { |
746 | 61 | JANUS_LOG(LOG_HUGE, " -- T/K bit is set!\n"); |
747 | | /* Read the TID/KEYIDX octet */ |
748 | 61 | buffer++; |
749 | 61 | vp8pd = *buffer; |
750 | 61 | } |
751 | 98 | } |
752 | 239 | buffer++; /* Now we're in the payload */ |
753 | 239 | if(sbit) { |
754 | 178 | JANUS_LOG(LOG_HUGE, " -- S bit is set!\n"); |
755 | 178 | unsigned long int vp8ph = 0; |
756 | 178 | memcpy(&vp8ph, buffer, 4); |
757 | 178 | vp8ph = ntohl(vp8ph); |
758 | 178 | uint8_t pbit = ((vp8ph & 0x01000000) >> 24); |
759 | 178 | if(!pbit) { |
760 | 109 | JANUS_LOG(LOG_HUGE, " -- P bit is NOT set!\n"); |
761 | | /* It is a key frame! Get resolution for debugging */ |
762 | 109 | unsigned char *c = (unsigned char *)buffer+3; |
763 | | /* vet via sync code */ |
764 | 109 | if(c[0]!=0x9d||c[1]!=0x01||c[2]!=0x2a) { |
765 | 108 | JANUS_LOG(LOG_HUGE, "First 3-bytes after header not what they're supposed to be?\n"); |
766 | 108 | } else { |
767 | 1 | unsigned short val3, val5; |
768 | 1 | memcpy(&val3,c+3,sizeof(short)); |
769 | 1 | int vp8w = swap2(val3)&0x3fff; |
770 | 1 | int vp8ws = swap2(val3)>>14; |
771 | 1 | memcpy(&val5,c+5,sizeof(short)); |
772 | 1 | int vp8h = swap2(val5)&0x3fff; |
773 | 1 | int vp8hs = swap2(val5)>>14; |
774 | 1 | JANUS_LOG(LOG_HUGE, "Got a VP8 key frame: %dx%d (scale=%dx%d)\n", vp8w, vp8h, vp8ws, vp8hs); |
775 | 1 | return TRUE; |
776 | 1 | } |
777 | 109 | } |
778 | 178 | } |
779 | | /* If we got here it's not a key frame */ |
780 | 238 | return FALSE; |
781 | 239 | } |
782 | | |
783 | 617 | gboolean janus_vp9_is_keyframe(const char *buffer, int len) { |
784 | 617 | if(!buffer || len < 16) |
785 | 378 | return FALSE; |
786 | | /* Parse VP9 header now */ |
787 | 239 | uint8_t vp9pd = *buffer; |
788 | 239 | uint8_t ibit = (vp9pd & 0x80); |
789 | 239 | uint8_t pbit = (vp9pd & 0x40); |
790 | 239 | uint8_t lbit = (vp9pd & 0x20); |
791 | 239 | uint8_t fbit = (vp9pd & 0x10); |
792 | 239 | uint8_t vbit = (vp9pd & 0x02); |
793 | 239 | buffer++; |
794 | 239 | len--; |
795 | 239 | if(ibit) { |
796 | | /* Read the PictureID octet */ |
797 | 98 | vp9pd = *buffer; |
798 | 98 | uint16_t picid = vp9pd, wholepicid = picid; |
799 | 98 | uint8_t mbit = (vp9pd & 0x80); |
800 | 98 | if(!mbit) { |
801 | 31 | buffer++; |
802 | 31 | len--; |
803 | 67 | } else { |
804 | 67 | memcpy(&picid, buffer, sizeof(uint16_t)); |
805 | 67 | wholepicid = ntohs(picid); |
806 | 67 | picid = (wholepicid & 0x7FFF); |
807 | 67 | buffer += 2; |
808 | 67 | len -= 2; |
809 | 67 | } |
810 | 98 | } |
811 | 239 | if(lbit) { |
812 | 134 | buffer++; |
813 | 134 | len--; |
814 | 134 | if(!fbit) { |
815 | | /* Non-flexible mode, skip TL0PICIDX */ |
816 | 10 | buffer++; |
817 | 10 | len--; |
818 | 10 | } |
819 | 134 | } |
820 | 239 | if(fbit && pbit) { |
821 | | /* Skip reference indices */ |
822 | 79 | uint8_t nbit = 1; |
823 | 1.18k | while(nbit) { |
824 | 1.11k | vp9pd = *buffer; |
825 | 1.11k | nbit = (vp9pd & 0x01); |
826 | 1.11k | buffer++; |
827 | 1.11k | len--; |
828 | 1.11k | if(len == 0) /* Make sure we don't overflow */ |
829 | 5 | return FALSE; |
830 | 1.11k | } |
831 | 79 | } |
832 | 234 | if(vbit) { |
833 | | /* Parse and skip SS */ |
834 | 153 | vp9pd = *buffer; |
835 | 153 | uint n_s = (vp9pd & 0xE0) >> 5; |
836 | 153 | n_s++; |
837 | 153 | uint8_t ybit = (vp9pd & 0x10); |
838 | 153 | if(ybit) { |
839 | | /* Iterate on all spatial layers and get resolution */ |
840 | 101 | buffer++; |
841 | 101 | len--; |
842 | 101 | if(len == 0) /* Make sure we don't overflow */ |
843 | 3 | return FALSE; |
844 | 98 | uint i=0; |
845 | 159 | for(i=0; i<n_s && len>=4; i++,len-=4) { |
846 | | /* Width */ |
847 | 147 | uint16_t w; |
848 | 147 | memcpy(&w, buffer, sizeof(uint16_t)); |
849 | 147 | int vp9w = ntohs(w); |
850 | 147 | buffer += 2; |
851 | | /* Height */ |
852 | 147 | uint16_t h; |
853 | 147 | memcpy(&h, buffer, sizeof(uint16_t)); |
854 | 147 | int vp9h = ntohs(h); |
855 | 147 | buffer += 2; |
856 | 147 | if(vp9w || vp9h) { |
857 | 86 | JANUS_LOG(LOG_HUGE, "Got a VP9 key frame: %dx%d\n", vp9w, vp9h); |
858 | 86 | return TRUE; |
859 | 86 | } |
860 | 147 | } |
861 | 98 | } |
862 | 153 | } |
863 | | /* If we got here it's not a key frame */ |
864 | 145 | return FALSE; |
865 | 234 | } |
866 | | |
867 | 617 | static gboolean janus_h264_contains_nal(const char *buffer, int len, int val) { |
868 | 617 | if(!buffer || len < 6) |
869 | 234 | return FALSE; |
870 | | /* Parse H264 header now */ |
871 | 383 | uint8_t fragment = *buffer & 0x1F; |
872 | 383 | uint8_t nal = *(buffer+1) & 0x1F; |
873 | 383 | if(fragment == val || ((fragment == 28 || fragment == 29) && nal == val && (*(buffer+1) & 0x80))) { |
874 | 10 | JANUS_LOG(LOG_HUGE, "Got an H264 NAL %d\n", val); |
875 | 10 | return TRUE; |
876 | 373 | } else if(fragment == 24) { |
877 | | /* May we find it in this STAP-A? */ |
878 | 60 | buffer++; |
879 | 60 | len--; |
880 | 60 | uint16_t psize = 0; |
881 | | /* We're reading 3 bytes */ |
882 | 446 | while(len > 2) { |
883 | 387 | memcpy(&psize, buffer, 2); |
884 | 387 | psize = ntohs(psize); |
885 | 387 | buffer += 2; |
886 | 387 | len -= 2; |
887 | 387 | int nal = *buffer & 0x1F; |
888 | 387 | if(nal == val) { |
889 | 1 | JANUS_LOG(LOG_HUGE, "Got an H264 NAL %d\n", val); |
890 | 1 | return TRUE; |
891 | 1 | } |
892 | 386 | buffer += psize; |
893 | 386 | len -= psize; |
894 | 386 | } |
895 | 60 | } |
896 | | /* If we got here we didn't find it */ |
897 | 372 | return FALSE; |
898 | 383 | } |
899 | | |
900 | 617 | gboolean janus_h264_is_keyframe(const char *buffer, int len) { |
901 | 617 | return janus_h264_contains_nal(buffer, len, 7); |
902 | 617 | } |
903 | | |
904 | 0 | gboolean janus_h264_is_i_frame(const char *buffer, int len) { |
905 | 0 | return janus_h264_contains_nal(buffer, len, 5); |
906 | 0 | } |
907 | | |
908 | 0 | gboolean janus_h264_is_b_frame(const char *buffer, int len) { |
909 | 0 | return janus_h264_contains_nal(buffer, len, 1); |
910 | 0 | } |
911 | | |
912 | 0 | gboolean janus_av1_is_keyframe(const char *buffer, int len) { |
913 | 0 | if(!buffer || len < 3) |
914 | 0 | return FALSE; |
915 | | /* Read the aggregation header */ |
916 | 0 | uint8_t aggrh = *buffer; |
917 | 0 | uint8_t zbit = (aggrh & 0x80) >> 7; |
918 | 0 | uint8_t nbit = (aggrh & 0x08) >> 3; |
919 | | /* FIXME Ugly hack: we consider a packet with Z=0 and N=1 a keyframe */ |
920 | 0 | return (!zbit && nbit); |
921 | 0 | } |
922 | | |
923 | 0 | gboolean janus_h265_is_keyframe(const char *buffer, int len) { |
924 | 0 | if(!buffer || len < 2) |
925 | 0 | return FALSE; |
926 | | /* Parse the NAL unit */ |
927 | 0 | uint16_t unit = 0; |
928 | 0 | memcpy(&unit, buffer, sizeof(uint16_t)); |
929 | 0 | unit = ntohs(unit); |
930 | 0 | uint8_t type = (unit & 0x7E00) >> 9; |
931 | 0 | if(type == 32 || type == 33 || type == 34 || type == 16 || type == 17 || type == 18 || type == 19 || type == 20 || type == 21) { |
932 | | /* FIXME We return TRUE for more than just VPS and SPS, as |
933 | | * suggested in https://github.com/meetecho/janus-gateway/issues/2323 */ |
934 | 0 | return TRUE; |
935 | 0 | } |
936 | 0 | return FALSE; |
937 | 0 | } |
938 | | |
939 | 0 | gboolean janus_is_keyframe(int codec, const char *buffer, int len) { |
940 | 0 | switch(codec) { |
941 | 0 | case JANUS_VIDEOCODEC_VP8: |
942 | 0 | return janus_vp8_is_keyframe(buffer, len); |
943 | 0 | case JANUS_VIDEOCODEC_VP9: |
944 | 0 | return janus_vp9_is_keyframe(buffer, len); |
945 | 0 | case JANUS_VIDEOCODEC_H264: |
946 | 0 | return janus_h264_is_keyframe(buffer, len); |
947 | 0 | case JANUS_VIDEOCODEC_AV1: |
948 | 0 | return janus_av1_is_keyframe(buffer, len); |
949 | 0 | case JANUS_VIDEOCODEC_H265: |
950 | 0 | return janus_h265_is_keyframe(buffer, len); |
951 | 0 | default: |
952 | 0 | break; |
953 | 0 | } |
954 | 0 | return FALSE; |
955 | 0 | } |
956 | | |
957 | | int janus_vp8_parse_descriptor(char *buffer, int len, |
958 | 1.23k | gboolean *m, uint16_t *picid, uint8_t *tl0picidx, uint8_t *tid, uint8_t *y, uint8_t *keyidx) { |
959 | 1.23k | if(!buffer || len < 6) |
960 | 468 | return -1; |
961 | 766 | if(picid) |
962 | 766 | *picid = 0; |
963 | 766 | if(tl0picidx) |
964 | 766 | *tl0picidx = 0; |
965 | 766 | if(tid) |
966 | 766 | *tid = 0; |
967 | 766 | if(y) |
968 | 766 | *y = 0; |
969 | 766 | if(keyidx) |
970 | 766 | *keyidx = 0; |
971 | 766 | uint8_t vp8pd = *buffer; |
972 | 766 | uint8_t xbit = (vp8pd & 0x80); |
973 | | /* Read the Extended control bits octet */ |
974 | 766 | if(xbit) { |
975 | 346 | buffer++; |
976 | 346 | vp8pd = *buffer; |
977 | 346 | uint8_t ibit = (vp8pd & 0x80); |
978 | 346 | uint8_t lbit = (vp8pd & 0x40); |
979 | 346 | uint8_t tbit = (vp8pd & 0x20); |
980 | 346 | uint8_t kbit = (vp8pd & 0x10); |
981 | 346 | if(ibit) { |
982 | | /* Read the PictureID octet */ |
983 | 214 | buffer++; |
984 | 214 | vp8pd = *buffer; |
985 | 214 | uint16_t partpicid = vp8pd, wholepicid = partpicid; |
986 | 214 | uint8_t mbit = (vp8pd & 0x80); |
987 | 214 | if(mbit) { |
988 | 146 | memcpy(&partpicid, buffer, sizeof(uint16_t)); |
989 | 146 | wholepicid = ntohs(partpicid); |
990 | 146 | partpicid = (wholepicid & 0x7FFF); |
991 | 146 | buffer++; |
992 | 146 | } |
993 | 214 | if(m) |
994 | 214 | *m = (mbit ? TRUE : FALSE); |
995 | 214 | if(picid) |
996 | 214 | *picid = partpicid; |
997 | 214 | } |
998 | 346 | if(lbit) { |
999 | | /* Read the TL0PICIDX octet */ |
1000 | 170 | buffer++; |
1001 | 170 | vp8pd = *buffer; |
1002 | 170 | if(tl0picidx) |
1003 | 170 | *tl0picidx = vp8pd; |
1004 | 170 | } |
1005 | 346 | if(tbit || kbit) { |
1006 | | /* Read the TID/Y/KEYIDX octet */ |
1007 | 204 | buffer++; |
1008 | 204 | vp8pd = *buffer; |
1009 | 204 | if(tid) |
1010 | 204 | *tid = (vp8pd & 0xC0) >> 6; |
1011 | 204 | if(y) |
1012 | 204 | *y = (vp8pd & 0x20) >> 5; |
1013 | 204 | if(keyidx) |
1014 | 204 | *keyidx = (vp8pd & 0x1F) >> 4; |
1015 | 204 | } |
1016 | 346 | } |
1017 | 766 | return 0; |
1018 | 1.23k | } |
1019 | | |
1020 | 383 | static int janus_vp8_replace_descriptor(char *buffer, int len, gboolean m, uint16_t picid, uint8_t tl0picidx) { |
1021 | 383 | if(!buffer || len < 6) |
1022 | 0 | return -1; |
1023 | 383 | uint8_t vp8pd = *buffer; |
1024 | 383 | uint8_t xbit = (vp8pd & 0x80); |
1025 | | /* Read the Extended control bits octet */ |
1026 | 383 | if(xbit) { |
1027 | 173 | buffer++; |
1028 | 173 | vp8pd = *buffer; |
1029 | 173 | uint8_t ibit = (vp8pd & 0x80); |
1030 | 173 | uint8_t lbit = (vp8pd & 0x40); |
1031 | 173 | uint8_t tbit = (vp8pd & 0x20); |
1032 | 173 | uint8_t kbit = (vp8pd & 0x10); |
1033 | 173 | if(ibit) { |
1034 | | /* Overwrite the PictureID octet */ |
1035 | 107 | buffer++; |
1036 | 107 | vp8pd = *buffer; |
1037 | 107 | uint8_t mbit = (vp8pd & 0x80); |
1038 | 107 | if(!mbit || !m) { |
1039 | 34 | *buffer = picid; |
1040 | 73 | } else { |
1041 | 73 | uint16_t wholepicid = htons(picid); |
1042 | 73 | memcpy(buffer, &wholepicid, 2); |
1043 | 73 | *buffer |= 0x80; |
1044 | 73 | buffer++; |
1045 | 73 | } |
1046 | 107 | } |
1047 | 173 | if(lbit) { |
1048 | | /* Overwrite the TL0PICIDX octet */ |
1049 | 85 | buffer++; |
1050 | 85 | *buffer = tl0picidx; |
1051 | 85 | } |
1052 | 173 | if(tbit || kbit) { |
1053 | | /* Should we overwrite the TID/Y/KEYIDX octet? */ |
1054 | 102 | buffer++; |
1055 | 102 | } |
1056 | 173 | } |
1057 | 383 | return 0; |
1058 | 383 | } |
1059 | | |
1060 | 0 | void janus_vp8_simulcast_context_reset(janus_vp8_simulcast_context *context) { |
1061 | 0 | if(context == NULL) |
1062 | 0 | return; |
1063 | | /* Reset the context values */ |
1064 | 0 | context->last_picid = 0; |
1065 | 0 | context->base_picid = 0; |
1066 | 0 | context->base_picid_prev = 0; |
1067 | 0 | context->last_tlzi = 0; |
1068 | 0 | context->base_tlzi = 0; |
1069 | 0 | context->base_tlzi_prev = 0; |
1070 | 0 | } |
1071 | | |
1072 | 617 | void janus_vp8_simulcast_descriptor_update(char *buffer, int len, janus_vp8_simulcast_context *context, gboolean switched) { |
1073 | 617 | if(!buffer || len < 0) |
1074 | 0 | return; |
1075 | 617 | gboolean m = FALSE; |
1076 | 617 | uint16_t picid = 0; |
1077 | 617 | uint8_t tlzi = 0; |
1078 | 617 | uint8_t tid = 0; |
1079 | 617 | uint8_t ybit = 0; |
1080 | 617 | uint8_t keyidx = 0; |
1081 | | /* Parse the identifiers in the VP8 payload descriptor */ |
1082 | 617 | if(janus_vp8_parse_descriptor(buffer, len, &m, &picid, &tlzi, &tid, &ybit, &keyidx) < 0) |
1083 | 234 | return; |
1084 | 383 | if(switched) { |
1085 | 383 | context->base_picid_prev = context->last_picid; |
1086 | 383 | context->base_picid = picid; |
1087 | 383 | context->base_tlzi_prev = context->last_tlzi; |
1088 | 383 | context->base_tlzi = tlzi; |
1089 | 383 | } |
1090 | 383 | context->last_picid = (picid-context->base_picid)+context->base_picid_prev+1; |
1091 | 383 | if(!m && context->last_picid > 127) { |
1092 | 0 | context->last_picid -= 128; |
1093 | 0 | if(context->last_picid > 127) |
1094 | 0 | context->last_picid = 0; |
1095 | 383 | } else if(m && context->last_picid > 32767) { |
1096 | 0 | context->last_picid -= 32768; |
1097 | 0 | } |
1098 | 383 | context->last_tlzi = (tlzi-context->base_tlzi)+context->base_tlzi_prev+1; |
1099 | | /* Overwrite the values in the VP8 payload descriptors with the ones we have */ |
1100 | 383 | janus_vp8_replace_descriptor(buffer, len, m, context->last_picid, context->last_tlzi); |
1101 | 383 | } |
1102 | | |
1103 | | /* Helper method to parse a VP9 RTP video frame and get some SVC-related info: |
1104 | | * notice that this only works with VP9, right now, on an experimental basis */ |
1105 | 617 | int janus_vp9_parse_svc(char *buffer, int len, gboolean *found, janus_vp9_svc_info *info) { |
1106 | 617 | if(!buffer || len < 8) |
1107 | 311 | return -1; |
1108 | | /* VP9 depay: */ |
1109 | | /* https://tools.ietf.org/html/draft-ietf-payload-vp9-04 */ |
1110 | | /* Read the first octet (VP9 Payload Descriptor) */ |
1111 | 306 | uint8_t vp9pd = *buffer; |
1112 | 306 | uint8_t ibit = (vp9pd & 0x80) >> 7; |
1113 | 306 | uint8_t pbit = (vp9pd & 0x40) >> 6; |
1114 | 306 | uint8_t lbit = (vp9pd & 0x20) >> 5; |
1115 | 306 | uint8_t fbit = (vp9pd & 0x10) >> 4; |
1116 | 306 | uint8_t bbit = (vp9pd & 0x08) >> 3; |
1117 | 306 | uint8_t ebit = (vp9pd & 0x04) >> 2; |
1118 | 306 | uint8_t vbit = (vp9pd & 0x02) >> 1; |
1119 | 306 | if(!lbit) { |
1120 | | /* No Layer indices present, no need to go on */ |
1121 | 115 | if(found) |
1122 | 115 | *found = FALSE; |
1123 | 115 | return 0; |
1124 | 115 | } |
1125 | | /* Move to the next octet and see what's there */ |
1126 | 191 | buffer++; |
1127 | 191 | len--; |
1128 | 191 | if(ibit) { |
1129 | | /* Read the PictureID octet */ |
1130 | 83 | vp9pd = *buffer; |
1131 | 83 | uint16_t picid = vp9pd, wholepicid = picid; |
1132 | 83 | uint8_t mbit = (vp9pd & 0x80); |
1133 | 83 | if(!mbit) { |
1134 | 34 | buffer++; |
1135 | 34 | len--; |
1136 | 49 | } else { |
1137 | 49 | memcpy(&picid, buffer, sizeof(uint16_t)); |
1138 | 49 | wholepicid = ntohs(picid); |
1139 | 49 | picid = (wholepicid & 0x7FFF); |
1140 | 49 | buffer += 2; |
1141 | 49 | len -= 2; |
1142 | 49 | } |
1143 | 83 | } |
1144 | 191 | if(lbit) { |
1145 | | /* Read the octet and parse the layer indices now */ |
1146 | 191 | vp9pd = *buffer; |
1147 | 191 | int tlid = (vp9pd & 0xE0) >> 5; |
1148 | 191 | uint8_t ubit = (vp9pd & 0x10) >> 4; |
1149 | 191 | int slid = (vp9pd & 0x0E) >> 1; |
1150 | 191 | uint8_t dbit = (vp9pd & 0x01); |
1151 | 191 | JANUS_LOG(LOG_HUGE, "%s Mode, Layer indices: Temporal: %d (u=%u), Spatial: %d (d=%u)\n", |
1152 | 191 | fbit ? "Flexible" : "Non-flexible", tlid, ubit, slid, dbit); |
1153 | 191 | if(info) { |
1154 | 191 | info->temporal_layer = tlid; |
1155 | 191 | info->spatial_layer = slid; |
1156 | 191 | info->fbit = fbit; |
1157 | 191 | info->pbit = pbit; |
1158 | 191 | info->dbit = dbit; |
1159 | 191 | info->ubit = ubit; |
1160 | 191 | info->bbit = bbit; |
1161 | 191 | info->ebit = ebit; |
1162 | 191 | } |
1163 | 191 | if(found) |
1164 | 191 | *found = TRUE; |
1165 | | /* Go on, just to get to the SS, if available (which we currently ignore anyway) */ |
1166 | 191 | buffer++; |
1167 | 191 | len--; |
1168 | 191 | if(!fbit) { |
1169 | | /* Non-flexible mode, skip TL0PICIDX */ |
1170 | 20 | buffer++; |
1171 | 20 | len--; |
1172 | 20 | } |
1173 | 191 | } |
1174 | 191 | if(fbit && pbit) { |
1175 | | /* Skip reference indices */ |
1176 | 86 | uint8_t nbit = 1; |
1177 | 928 | while(nbit) { |
1178 | 849 | vp9pd = *buffer; |
1179 | 849 | nbit = (vp9pd & 0x01); |
1180 | 849 | buffer++; |
1181 | 849 | len--; |
1182 | 849 | if(len == 0) /* Make sure we don't overflow */ |
1183 | 7 | return -1; |
1184 | 849 | } |
1185 | 86 | } |
1186 | 184 | if(vbit) { |
1187 | | /* Parse and skip SS */ |
1188 | 147 | vp9pd = *buffer; |
1189 | 147 | int n_s = (vp9pd & 0xE0) >> 5; |
1190 | 147 | n_s++; |
1191 | 147 | JANUS_LOG(LOG_HUGE, "There are %d spatial layers\n", n_s); |
1192 | 147 | uint8_t ybit = (vp9pd & 0x10); |
1193 | 147 | uint8_t gbit = (vp9pd & 0x08); |
1194 | 147 | if(ybit) { |
1195 | | /* Iterate on all spatial layers and get resolution */ |
1196 | 69 | buffer++; |
1197 | 69 | len--; |
1198 | 69 | if(len == 0) /* Make sure we don't overflow */ |
1199 | 5 | return -1; |
1200 | 64 | int i=0; |
1201 | 255 | for(i=0; i<n_s; i++) { |
1202 | | /* Been there, done that: skip skip skip */ |
1203 | 218 | buffer += 4; |
1204 | 218 | len -= 4; |
1205 | 218 | if(len <= 0) /* Make sure we don't overflow */ |
1206 | 27 | return -1; |
1207 | 218 | } |
1208 | 64 | } |
1209 | 115 | if(gbit) { |
1210 | 84 | if(!ybit) { |
1211 | 63 | buffer++; |
1212 | 63 | len--; |
1213 | 63 | if(len == 0) /* Make sure we don't overflow */ |
1214 | 1 | return -1; |
1215 | 63 | } |
1216 | 83 | uint8_t n_g = *buffer; |
1217 | 83 | JANUS_LOG(LOG_HUGE, "There are %u frames in a GOF\n", n_g); |
1218 | 83 | buffer++; |
1219 | 83 | len--; |
1220 | 83 | if(len == 0) /* Make sure we don't overflow */ |
1221 | 2 | return -1; |
1222 | 81 | if(n_g > 0) { |
1223 | 77 | int i=0; |
1224 | 3.95k | for(i=0; i<n_g; i++) { |
1225 | | /* Read the R bits */ |
1226 | 3.93k | vp9pd = *buffer; |
1227 | 3.93k | int r = (vp9pd & 0x0C) >> 2; |
1228 | 3.93k | if(r > 0) { |
1229 | | /* Skip reference indices */ |
1230 | 2.13k | buffer += r; |
1231 | 2.13k | len -= r; |
1232 | 2.13k | if(len <= 0) /* Make sure we don't overflow */ |
1233 | 21 | return -1; |
1234 | 2.13k | } |
1235 | 3.91k | buffer++; |
1236 | 3.91k | len--; |
1237 | 3.91k | if(len == 0) /* Make sure we don't overflow */ |
1238 | 37 | return -1; |
1239 | 3.91k | } |
1240 | 77 | } |
1241 | 81 | } |
1242 | 115 | } |
1243 | 91 | return 0; |
1244 | 184 | } |
1245 | | |
1246 | | /* RED parsing and building utilities */ |
1247 | 0 | GList *janus_red_parse_blocks(char *buffer, int len) { |
1248 | 0 | if(buffer == NULL || len < 0) |
1249 | 0 | return NULL; |
1250 | | /* TODO This whole method should be fuzzed */ |
1251 | 0 | char *payload = buffer; |
1252 | 0 | int plen = len; |
1253 | | /* Find out how many generations are in the RED packet */ |
1254 | 0 | int gens = 0; |
1255 | 0 | uint32_t red_block; |
1256 | 0 | uint8_t follow = 0, block_pt = 0; |
1257 | 0 | uint16_t ts_offset = 0, block_len = 0; |
1258 | 0 | GList *blocks = NULL; |
1259 | 0 | janus_red_block *rb = NULL; |
1260 | | /* Parse the header */ |
1261 | 0 | while(payload != NULL && plen > 0) { |
1262 | | /* Go through the header for the different generations */ |
1263 | 0 | gens++; |
1264 | 0 | follow = ((*payload) & 0x80) >> 7; |
1265 | 0 | block_pt = (*payload) & 0x7F; |
1266 | 0 | if(follow && plen > 3) { |
1267 | | /* Read the rest of the header */ |
1268 | 0 | memcpy(&red_block, payload, sizeof(red_block)); |
1269 | 0 | red_block = ntohl(red_block); |
1270 | 0 | ts_offset = (red_block & 0x00FFFC00) >> 10; |
1271 | 0 | block_len = (red_block & 0x000003FF); |
1272 | 0 | JANUS_LOG(LOG_HUGE, " [%d] f=%u, pt=%u, tsoff=%"SCNu16", blen=%"SCNu16"\n", |
1273 | 0 | gens, follow, block_pt, ts_offset, block_len); |
1274 | 0 | rb = g_malloc0(sizeof(janus_red_block)); |
1275 | 0 | rb->pt = block_pt; |
1276 | 0 | rb->ts_offset = ts_offset; |
1277 | 0 | rb->length = block_len; |
1278 | 0 | blocks = g_list_append(blocks, rb); |
1279 | 0 | payload += 4; |
1280 | 0 | plen -= 4; |
1281 | 0 | } else { |
1282 | | /* Header parsed */ |
1283 | 0 | payload++; |
1284 | 0 | plen--; |
1285 | 0 | JANUS_LOG(LOG_HUGE, " [%d] f=%u, pt=%u, tsoff=0, blen=TBD.\n", |
1286 | 0 | gens, follow, block_pt); |
1287 | 0 | break; |
1288 | 0 | } |
1289 | 0 | } |
1290 | | /* Go through the blocks, iterating on the lengths to get a pointer to the data */ |
1291 | 0 | if(blocks != NULL) { |
1292 | 0 | gens = 0; |
1293 | 0 | uint16_t length = 0; |
1294 | 0 | GList *temp = blocks; |
1295 | 0 | while(temp != NULL) { |
1296 | 0 | gens++; |
1297 | 0 | rb = (janus_red_block *)temp->data; |
1298 | 0 | length = rb->length; |
1299 | 0 | if(length > plen) { |
1300 | 0 | JANUS_LOG(LOG_WARN, " >> [%d] Broken red payload:\n", gens); |
1301 | 0 | g_list_free_full(blocks, (GDestroyNotify)g_free); |
1302 | 0 | return NULL; |
1303 | 0 | } |
1304 | 0 | if(length > 0) { |
1305 | | /* Redundant data, take note of where the block is */ |
1306 | 0 | JANUS_LOG(LOG_HUGE, " >> [%d] plen=%"SCNu16"\n", gens, length); |
1307 | 0 | rb->data = (uint8_t *)payload; |
1308 | 0 | payload += length; |
1309 | 0 | plen -= length; |
1310 | 0 | } |
1311 | 0 | temp = temp->next; |
1312 | 0 | } |
1313 | 0 | } |
1314 | 0 | if(plen > 0) { |
1315 | | /* The last block is the primary data, add it to the list */ |
1316 | 0 | gens++; |
1317 | 0 | JANUS_LOG(LOG_HUGE, " >> [%d] plen=%d\n", gens, plen); |
1318 | 0 | rb = g_malloc0(sizeof(janus_red_block)); |
1319 | 0 | rb->pt = block_pt; |
1320 | 0 | rb->length = plen; |
1321 | 0 | rb->data = (uint8_t *)payload; |
1322 | 0 | blocks = g_list_append(blocks, rb); |
1323 | 0 | } |
1324 | |
|
1325 | 0 | return blocks; |
1326 | 0 | } |
1327 | 0 | int janus_red_pack_blocks(char *buffer, int len, GList *blocks) { |
1328 | 0 | if(buffer == NULL || len < 0) |
1329 | 0 | return 1; |
1330 | 0 | int required = 0, written = 0; |
1331 | 0 | janus_red_block *rb = NULL; |
1332 | | /* Write all headers to the buffer */ |
1333 | 0 | uint32_t red_block = 0; |
1334 | 0 | uint8_t *payload = (uint8_t *)buffer; |
1335 | 0 | GList *temp = blocks; |
1336 | 0 | while(temp != NULL) { |
1337 | 0 | rb = (janus_red_block *)temp->data; |
1338 | 0 | required += (temp->next ? 4 : 1); |
1339 | 0 | required += rb->length; |
1340 | 0 | if(len < required) { |
1341 | 0 | JANUS_LOG(LOG_ERR, "RED buffer too small (%d bytes, at least %d needed)\n", len, required); |
1342 | 0 | return -2; |
1343 | 0 | } |
1344 | 0 | if(temp->next != NULL) { |
1345 | | /* There's going to be a follow-up, write 4 bytes (F=1 and info) */ |
1346 | 0 | red_block = |
1347 | 0 | 0x80000000 + /* F=1 */ |
1348 | 0 | (0x7F000000 & (rb->pt << 24)) + /* Payload type */ |
1349 | 0 | (0x00FFFC00 & (rb->ts_offset << 10)) + /* Timestamp offset */ |
1350 | 0 | (0x000003FF & rb->length); /* Data length */ |
1351 | 0 | red_block = htonl(red_block); |
1352 | 0 | memcpy(payload + written, &red_block, sizeof(red_block)); |
1353 | 0 | written += 4; |
1354 | 0 | } else { |
1355 | | /* Primary data, 1 byte (F=0 and payload type) */ |
1356 | 0 | uint8_t pt = rb->pt; |
1357 | 0 | *(payload + written) = pt; |
1358 | 0 | written++; |
1359 | 0 | } |
1360 | 0 | temp = temp->next; |
1361 | 0 | } |
1362 | | /* Now write all data to the buffer too */ |
1363 | 0 | temp = blocks; |
1364 | 0 | while(temp != NULL) { |
1365 | 0 | rb = (janus_red_block *)temp->data; |
1366 | | /* Write the data itself */ |
1367 | 0 | memcpy(payload + written, rb->data, rb->length); |
1368 | 0 | written += rb->length; |
1369 | 0 | temp = temp->next; |
1370 | 0 | } |
1371 | 0 | return written; |
1372 | 0 | } |
1373 | 0 | int janus_red_replace_block_pt(char *buffer, int len, int pt) { |
1374 | 0 | if(buffer == NULL || len < 0 || pt < 0 || pt > 127) |
1375 | 0 | return -1; |
1376 | | /* TODO This whole method should be fuzzed */ |
1377 | 0 | char *payload = buffer; |
1378 | 0 | int plen = len; |
1379 | 0 | uint8_t follow = 0; |
1380 | | /* Parse the header */ |
1381 | 0 | while(payload != NULL && plen > 0) { |
1382 | | /* Go through the block headers */ |
1383 | 0 | follow = ((*payload) & 0x80) >> 7; |
1384 | 0 | *payload = (0x80 & (follow << 7)) + (0x7F & pt); |
1385 | 0 | if(follow && plen > 3) { |
1386 | | /* Move to the next block header */ |
1387 | 0 | payload += 4; |
1388 | 0 | plen -= 4; |
1389 | 0 | } else { |
1390 | | /* We're done */ |
1391 | 0 | break; |
1392 | 0 | } |
1393 | 0 | } |
1394 | 0 | return 0; |
1395 | 0 | } |
1396 | | |
1397 | | /* Bit manipulation (mostly for TWCC) */ |
1398 | 0 | inline guint32 janus_push_bits(guint32 word, size_t num, guint32 val) { |
1399 | 0 | if(num == 0) |
1400 | 0 | return word; |
1401 | 0 | return (word << num) | (val & (0xFFFFFFFF>>(32-num))); |
1402 | 0 | } |
1403 | | |
1404 | 0 | inline void janus_set1(guint8 *data,size_t i,guint8 val) { |
1405 | 0 | data[i] = val; |
1406 | 0 | } |
1407 | | |
1408 | 0 | inline void janus_set2(guint8 *data,size_t i,guint32 val) { |
1409 | 0 | data[i+1] = (guint8)(val); |
1410 | 0 | data[i] = (guint8)(val>>8); |
1411 | 0 | } |
1412 | | |
1413 | 0 | inline void janus_set3(guint8 *data,size_t i,guint32 val) { |
1414 | 0 | data[i+2] = (guint8)(val); |
1415 | 0 | data[i+1] = (guint8)(val>>8); |
1416 | 0 | data[i] = (guint8)(val>>16); |
1417 | 0 | } |
1418 | | |
1419 | 0 | inline void janus_set4(guint8 *data,size_t i,guint32 val) { |
1420 | 0 | data[i+3] = (guint8)(val); |
1421 | 0 | data[i+2] = (guint8)(val>>8); |
1422 | 0 | data[i+1] = (guint8)(val>>16); |
1423 | 0 | data[i] = (guint8)(val>>24); |
1424 | 0 | } |
1425 | | |
1426 | 0 | uint8_t janus_bitstream_getbit(uint8_t *base, uint32_t offset) { |
1427 | 0 | return ((*(base + (offset >> 0x3))) >> (0x7 - (offset & 0x7))) & 0x1; |
1428 | 0 | } |
1429 | | |
1430 | 0 | uint32_t janus_bitstream_getbits(uint8_t *base, uint8_t num, uint32_t *offset) { |
1431 | 0 | uint32_t res = 0; |
1432 | 0 | int32_t i = 0; |
1433 | 0 | for(i=num-1; i>=0; i--) { |
1434 | 0 | res |= janus_bitstream_getbit(base, (*offset)++) << i; |
1435 | 0 | } |
1436 | 0 | return res; |
1437 | 0 | } |
1438 | | |
1439 | | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
1440 | | size_t janus_gzip_compress(int compression, char *text, size_t tlen, char *compressed, size_t zlen) { |
1441 | | if(text == NULL || tlen < 1 || compressed == NULL || zlen < 1) |
1442 | | return 0; |
1443 | | if(compression < 0 || compression > 9) { |
1444 | | JANUS_LOG(LOG_WARN, "Invalid compression factor %d, falling back to default compression...\n", compression); |
1445 | | compression = Z_DEFAULT_COMPRESSION; |
1446 | | } |
1447 | | |
1448 | | /* Initialize the deflater, and clarify we need gzip */ |
1449 | | z_stream zs = { 0 }; |
1450 | | zs.zalloc = Z_NULL; |
1451 | | zs.zfree = Z_NULL; |
1452 | | zs.opaque = Z_NULL; |
1453 | | zs.next_in = (Bytef *)text; |
1454 | | zs.avail_in = (uInt)tlen; |
1455 | | zs.next_out = (Bytef *)compressed; |
1456 | | zs.avail_out = (uInt)zlen; |
1457 | | int res = deflateInit2(&zs, compression, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY); |
1458 | | if(res != Z_OK) { |
1459 | | JANUS_LOG(LOG_ERR, "deflateInit error: %d\n", res); |
1460 | | return 0; |
1461 | | } |
1462 | | /* Deflate the string */ |
1463 | | res = deflate(&zs, Z_FINISH); |
1464 | | if(res != Z_STREAM_END) { |
1465 | | JANUS_LOG(LOG_ERR, "deflate error: %d\n", res); |
1466 | | return 0; |
1467 | | } |
1468 | | res = deflateEnd(&zs); |
1469 | | if(res != Z_OK) { |
1470 | | JANUS_LOG(LOG_ERR, "deflateEnd error: %d\n", res); |
1471 | | return 0; |
1472 | | } |
1473 | | |
1474 | | /* Done, return the size of the compressed data */ |
1475 | | return zs.total_out; |
1476 | | } |
1477 | | #endif |