forked from meetecho/janus-gateway
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.h
420 lines (365 loc) · 20.4 KB
/
utils.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
/*! \file utils.h
* \author Lorenzo Miniero <[email protected]>
* \copyright GNU General Public License v3
* \brief Utilities and helpers (headers)
* \details Implementations of a few methods that may be of use here
* and there in the code.
*
* \ingroup core
* \ref core
*/
#ifndef JANUS_UTILS_H
#define JANUS_UTILS_H
#include <stdint.h>
#include <glib.h>
#include <jansson.h>
#define JANUS_JSON_STRING JSON_STRING
#define JANUS_JSON_INTEGER JSON_INTEGER
#define JANUS_JSON_OBJECT JSON_OBJECT
/* Use JANUS_JSON_BOOL instead of the non-existing JSON_BOOLEAN */
#define JANUS_JSON_BOOL JSON_TRUE
#define JANUS_JSON_PARAM_REQUIRED 1
#define JANUS_JSON_PARAM_POSITIVE 2
#define JANUS_JSON_PARAM_NONEMPTY 4
struct janus_json_parameter {
const gchar *name;
json_type jtype;
unsigned int flags;
};
/*! \brief Helper to retrieve the system monotonic time, as Glib's
* g_get_monotonic_time may not be available (only since 2.28)
* @returns The system monotonic time */
gint64 janus_get_monotonic_time(void);
/*! \brief Helper to retrieve the system real time, as Glib's
* g_get_real_time may not be available (only since 2.28)
* @returns The system real time */
gint64 janus_get_real_time(void);
/*! \brief Helper to replace strings
* @param message The string that contains the text to replace, which may be
* freed if it is too short
* @param old_string The old text to replace
* @param new_string The new text
* @returns A pointer to the updated text string (re-allocated or just updated) */
char *janus_string_replace(char *message, const char *old_string, const char *new_string) G_GNUC_WARN_UNUSED_RESULT;
/*! \brief Helper to parse yes/no|true/false configuration values
* @param value The configuration value to parse
* @returns true if the value contains a "yes", "YES", "true", TRUE", "1", false otherwise */
gboolean janus_is_true(const char *value);
/*! \brief Helper to compare strings in constant time
* @param str1 The first string to compare
* @param str2 The second string to compare
* @returns true if the strings are the same, false otherwise */
gboolean janus_strcmp_const_time(const void *str1, const void *str2);
/*! \brief Helper to generate random 32-bit unsigned integers (useful for SSRCs, etc.)
* @note Warning: this will fall back to a non-cryptographically safe PRNG in case
* the crypto library RAND_bytes() call fails.
* @returns A (mostly crypto-safe) random 32-bit unsigned integer */
guint32 janus_random_uint32(void);
/*! \brief Helper to generate random 64-bit unsigned integers
* @note Unlike janus_random_uint64(), which actually only generates 52 bits, this
* generates the full 64 bits. See the janus_random_uint64() docstring for details.
* Warning: this will fall back to a non-cryptographically safe PRNG in case
* the crypto library RAND_bytes() call fails.
* @returns A (mostly crypto-safe) random 52-bit unsigned integer */
guint64 janus_random_uint64_full(void);
/*! \brief Helper to generate random 52 bit unsigned integers
* @note The reason for 52 instead of 64 bits: Javascript does not have real integers,
* its builtin "number" type is a float64. Thus, only integer values up to
* <code>Number.MAX_SAFE_INTEGER == 2^53 - 1 == 9007199254740991</code>
* can be safely represented in Javascript. This method returns such numbers.
* Use this method instead of janus_random_uint64_full() whenever you generate numbers which
* might end up in Javascript (via JSON API).
* This method is called janus_random_uint64() instead of janus_random_uint52() (or similar)
* for backwards compatibility.
* Warning: this will fall back to a non-cryptographically safe PRNG in case
* the crypto library RAND_bytes() call fails.
* @returns A (mostly crypto-safe) random 64-bit unsigned integer */
guint64 janus_random_uint64(void);
/*! \brief Helper to generate random UUIDs (needed by some plugins)
* Warning: this will fall back to a non-cryptographically safe PRNG in case
* the crypto library RAND_bytes() call fails.
* @returns A (mostly crypto-safe) random UUID string, which must be deallocated with \c g_free */
char *janus_random_uuid(void);
/*! \brief Helper to generate an allocated copy of a guint64 number
* @note While apparently silly, this is needed in order to make sure guint64 values
* used as keys in GHashTable operations are not lost: using temporary guint64 numbers
* in a g_hash_table_insert, for instance, will cause the key to contain garbage as
* soon as the temporary variable is lost, and all opererations on the key to fail
* @param num The guint64 number to duplicate
* @returns A pointer to a guint64 number, if successful, NULL otherwise */
guint64 *janus_uint64_dup(guint64 num);
/*! \brief Helper to hash a guint64 number to another guint64 number
* @note We currently only use for event handlers
* @param
* @returns The hashed number */
guint64 janus_uint64_hash(guint64 num);
/*! \brief Helper method to convert a string to a uint8_t
* @note The value of \c num should be ignored, if the method returned an error
* @param[in] str The string to convert
* @param[out] num Pointer to the converted number
* @returns 0 if successful, or a negative integer otherwise (e.g., \c -ERANGE if the value is out of range) */
int janus_string_to_uint8(const char *str, uint8_t *num);
/*! \brief Helper method to convert a string to a uint16_t
* @note The value of \c num should be ignored, if the method returned an error
* @param[in] str The string to convert
* @param[out] num Pointer to the converted number
* @returns 0 if successful, or a negative integer otherwise (e.g., \c -ERANGE if the value is out of range) */
int janus_string_to_uint16(const char *str, uint16_t *num);
/*! \brief Helper method to convert a string to a uint32_t
* @note The value of \c num should be ignored, if the method returned an error
* @param[in] str The string to convert
* @param[out] num Pointer to the converted number
* @returns 0 if successful, or a negative integer otherwise (e.g., \c -ERANGE if the value is out of range) */
int janus_string_to_uint32(const char *str, uint32_t *num);
/** @name Flags helper methods
*/
///@{
/*! \brief Janus flags container */
typedef gsize janus_flags;
/*! \brief Janus flags reset method
* \param[in] flags The janus_flags instance to reset */
void janus_flags_reset(janus_flags *flags);
/*! \brief Janus flags set method
* \param[in] flags The janus_flags instance to update
* \param[in] flag The flag to set */
void janus_flags_set(janus_flags *flags, gsize flag);
/*! \brief Janus flags clear method
* \param[in] flags The janus_flags instance to update
* \param[in] flag The flag to clear */
void janus_flags_clear(janus_flags *flags, gsize flag);
/*! \brief Janus flags check method
* \param[in] flags The janus_flags instance to check
* \param[in] flag The flag to check
* \returns true if the flag is set, false otherwise */
gboolean janus_flags_is_set(janus_flags *flags, gsize flag);
///@}
/*! \brief Helper to create a new directory, and recursively create parent directories if needed
* @param dir Path to the new folder to create
* @param mode File permissions for the new directory file
* @returns An integer like the regular mkdir does
* @note A failure may indicate that creating any of the subdirectories failed: some may still have been created */
int janus_mkdir(const char *dir, mode_t mode);
/*! \brief Helper to convert \c path relative to \c base_dir to absolute path.
* If \c path already represents absolute path then just g_strdup it.
* @param[in] base_dir Path which will be prepended to \c path if it's relative
* @param[in] path Some relative or absolute path
* @returns g_strdup'ed absolute path. Should be freed with g_free() when no longer needed */
gchar *janus_make_absolute_path(const gchar *base_dir, const gchar *path);
/*! \brief Ugly and dirty helper to quickly get the payload type associated with a codec in an SDP
* @param sdp The SDP to parse
* @param codec The codec to look for
* @returns The payload type, if found, -1 otherwise */
int janus_get_codec_pt(const char *sdp, const char *codec);
/*! \brief Ugly and dirty helper to quickly get the codec associated with a payload type in an SDP
* @param sdp The SDP to parse
* @param pt The payload type to look for
* @returns The codec name, if found, NULL otherwise */
const char *janus_get_codec_from_pt(const char *sdp, int pt);
/*! \brief Create and lock a PID file
* @param file Path to the PID file to use
* @returns 0 if successful, a negative integer otherwise */
int janus_pidfile_create(const char *file);
/*! \brief Unlock and remove a previously created PID file
* @returns 0 if successful, a negative integer otherwise */
int janus_pidfile_remove(void);
/*! \brief Add a folder to the protected list (meaning we won't create
* files there, like recordings or pcap dumps)
* @param folder Folder to protect */
void janus_protected_folder_add(const char *folder);
/*! \brief Check if the path points to a protected folder
* @param path Path we need to check
* @returns TRUE if the folder is protected, and FALSE otherwise */
gboolean janus_is_folder_protected(const char *path);
/*! \brief Cleanup the list of protected folder */
void janus_protected_folders_clear(void);
/*! \brief Creates a string describing the JSON type and constraint
* @param jtype The JSON type, e.g., JSON_STRING
* @param flags Indicates constraints for the described type
* @param[out] type_name The type description, e.g., "a positive integer"; required size is 19 characters
* @returns 0 if successful, a negative integer otherwise */
void janus_get_json_type_name(int jtype, unsigned int flags, char *type_name);
/*! \brief Checks whether the JSON value matches the type and constraint
* @param val The JSON value to be checked
* @param jtype The JSON type, e.g., JSON_STRING
* @param flags Indicates constraints for the described type
* @returns TRUE if the value is valid */
gboolean janus_json_is_valid(json_t *val, json_type jtype, unsigned int flags);
/*! \brief Validates the JSON object against the description of its parameters
* @param missing_format printf format to indicate a missing required parameter; needs one %s for the parameter name
* @param invalid_format printf format to indicate an invalid parameter; needs two %s for parameter name and type description from janus_get_json_type_name
* @param obj The JSON object to be validated
* @param params Array of struct janus_json_parameter to describe the parameters; the array has to be a global or stack variable to make sizeof work
* @param[out] error_code int to return error code
* @param[out] error_cause Array of char or NULL to return the error descriptions; the array has to be a global or stack variable to make sizeof work; the required size is the length of the format string plus the length of the longest parameter name plus 19 for the type description
* @param log_error If TRUE, log any error with JANUS_LOG(LOG_ERR)
* @param missing_code The code to be returned in error_code if a parameter is missing
* @param invalid_code The code to be returned in error_code if a parameter is invalid */
#define JANUS_VALIDATE_JSON_OBJECT_FORMAT(missing_format, invalid_format, obj, params, error_code, error_cause, log_error, missing_code, invalid_code) \
do { \
error_code = 0; \
unsigned int i; \
for(i = 0; i < sizeof(params) / sizeof(struct janus_json_parameter); i++) { \
json_t *val = json_object_get(obj, params[i].name); \
if(!val) { \
if((params[i].flags & JANUS_JSON_PARAM_REQUIRED) != 0) { \
error_code = (missing_code); \
if(log_error) \
JANUS_LOG(LOG_ERR, missing_format "\n", params[i].name); \
if(error_cause != NULL) \
g_snprintf(error_cause, sizeof(error_cause), missing_format, params[i].name); \
break; \
} \
continue; \
} \
if(!janus_json_is_valid(val, params[i].jtype, params[i].flags)) { \
error_code = (invalid_code); \
char type_name[20]; \
janus_get_json_type_name(params[i].jtype, params[i].flags, type_name); \
if(log_error) \
JANUS_LOG(LOG_ERR, invalid_format "\n", params[i].name, type_name); \
if(error_cause != NULL) \
g_snprintf(error_cause, sizeof(error_cause), invalid_format, params[i].name, type_name); \
break; \
} \
} \
} while(0)
/*! \brief Validates the JSON object against the description of its parameters
* @param obj The JSON object to be validated
* @param params Array of struct janus_json_parameter to describe the parameters; the array has to be a global or stack variable to make sizeof work
* @param[out] error_code int to return error code
* @param[out] error_cause Array of char or NULL to return the error descriptions; the array has to be a global or stack variable to make sizeof work; the required size is the length of the longest parameter name plus 54 for the format string and type description
* @param log_error If TRUE, log any error with JANUS_LOG(LOG_ERR)
* @param missing_code The code to be returned in error_code if a parameter is missing
* @param invalid_code The code to be returned in error_code if a parameter is invalid */
#define JANUS_VALIDATE_JSON_OBJECT(obj, params, error_code, error_cause, log_error, missing_code, invalid_code) \
JANUS_VALIDATE_JSON_OBJECT_FORMAT("Missing mandatory element (%s)", "Invalid element type (%s should be %s)", obj, params, error_code, error_cause, log_error, missing_code, invalid_code)
/*! \brief If the secret isn't NULL, check the secret after validating the specified member of the JSON object
* @param secret The secret to be checked; no check if the secret is NULL
* @param obj The JSON object to be validated
* @param member The JSON member with the secret, usually "secret" or "pin"
* @param[out] error_code int to return error code
* @param[out] error_cause Array of char or NULL to return the error descriptions; the array has to be a global or stack variable to make sizeof work; the required size is 60
* @param missing_code The code to be returned in error_code if a parameter is missing
* @param invalid_code The code to be returned in error_code if a parameter is invalid
* @param unauthorized_code The code to be returned in error_code if the secret doesn't match */
#define JANUS_CHECK_SECRET(secret, obj, member, error_code, error_cause, missing_code, invalid_code, unauthorized_code) \
do { \
if (secret) { \
static struct janus_json_parameter secret_parameters[] = { \
{member, JSON_STRING, JANUS_JSON_PARAM_REQUIRED} \
}; \
JANUS_VALIDATE_JSON_OBJECT(obj, secret_parameters, error_code, error_cause, TRUE, missing_code, invalid_code); \
if(error_code == 0 && !janus_strcmp_const_time((secret), json_string_value(json_object_get(obj, member)))) { \
error_code = (unauthorized_code); \
JANUS_LOG(LOG_ERR, "Unauthorized (wrong %s)\n", member); \
if(error_cause != NULL) \
g_snprintf(error_cause, sizeof(error_cause), "Unauthorized (wrong %s)", member); \
} \
} \
} while(0)
/*! \brief Helper method to check if a VP8 frame is a keyframe or not
* @param[in] buffer The RTP payload to process
* @param[in] len The length of the RTP payload
* @returns TRUE if it's a keyframe, FALSE otherwise */
gboolean janus_vp8_is_keyframe(const char *buffer, int len);
/*! \brief Helper method to check if a VP9 frame is a keyframe or not
* @param[in] buffer The RTP payload to process
* @param[in] len The length of the RTP payload
* @returns TRUE if it's a keyframe, FALSE otherwise */
gboolean janus_vp9_is_keyframe(const char *buffer, int len);
/*! \brief Helper method to check if an H.264 frame is a keyframe or not
* @param[in] buffer The RTP payload to process
* @param[in] len The length of the RTP payload
* @returns TRUE if it's a keyframe, FALSE otherwise */
gboolean janus_h264_is_keyframe(const char *buffer, int len);
/*! \brief Helper method to check if an AV1 frame is a keyframe or not
* @note Currently only a placeholder, always returns FALSE
* @param[in] buffer The RTP payload to process
* @param[in] len The length of the RTP payload
* @returns TRUE if it's a keyframe, FALSE otherwise */
gboolean janus_av1_is_keyframe(const char *buffer, int len);
/*! \brief Helper method to check if an H.265 frame is a keyframe or not
* @note Currently only a placeholder, always returns FALSE
* @param[in] buffer The RTP payload to process
* @param[in] len The length of the RTP payload
* @returns TRUE if it's a keyframe, FALSE otherwise */
gboolean janus_h265_is_keyframe(const char *buffer, int len);
/*! \brief VP8 simulcasting context, in order to make sure SSRC changes result in coherent picid/temporal level increases */
typedef struct janus_vp8_simulcast_context {
uint16_t last_picid, base_picid, base_picid_prev;
uint8_t last_tlzi, base_tlzi, base_tlzi_prev;
} janus_vp8_simulcast_context;
/*! \brief Set (or reset) the context fields to their default values
* @param[in] context The context to (re)set */
void janus_vp8_simulcast_context_reset(janus_vp8_simulcast_context *context);
/*! \brief Helper method to parse a VP8 payload descriptor for useful info (e.g., when simulcasting)
* @param[in] buffer The RTP payload to process
* @param[in] len The length of the RTP payload
* @param[out] picid The Picture ID
* @param[out] tl0picidx Temporal level zero index
* @param[out] tid Temporal-layer index
* @param[out] y Layer sync bit
* @param[out] keyidx Temporal key frame index
* @returns 0 in case of success, a negative integer otherwise */
int janus_vp8_parse_descriptor(char *buffer, int len,
uint16_t *picid, uint8_t *tl0picidx, uint8_t *tid, uint8_t *y, uint8_t *keyidx);
/*! \brief Use the context info to update the RTP header of a packet, if needed
* @param[in] buffer The RTP payload to process
* @param[in] len The length of the RTP payload
* @param[in] context The context to use as a reference
* @param[in] switched Whether there has been a source switch or not (important to compute offsets) */
void janus_vp8_simulcast_descriptor_update(char *buffer, int len, janus_vp8_simulcast_context *context, gboolean switched);
/*! \brief VP9 SVC info, as parsed from a payload descriptor */
typedef struct janus_vp9_svc_info {
int spatial_layer, temporal_layer;
uint8_t fbit, pbit, dbit, ubit, bbit, ebit;
} janus_vp9_svc_info;
/*! \brief Helper method to parse a VP9 payload descriptor for SVC-related info (e.g., when SVC is enabled)
* @param[in] buffer The RTP payload to process
* @param[in] len The length of the RTP payload
* @param[out] found Whether any SVC related info has been found or not
* @param[out] info Pointer to a janus_vp9_svc_info structure for passing the parsed info back
* @returns 0 in case of success, a negative integer otherwise */
int janus_vp9_parse_svc(char *buffer, int len, gboolean *found, janus_vp9_svc_info *info);
/*! \brief Helper method to push individual bits at the end of a word
* @param[in] word Initial value of word
* @param[in] num Number of bits to push
* @param[in] val Value of bits to push
* @returns 0 New word value*/
guint32 janus_push_bits(guint32 word, size_t num, guint32 val);
/*! \brief Helper method to set one byte at a memory position
* @param[in] data memory data pointer
* @param[in] i position in memory to change
* @param[in] val value to write
*/
void janus_set1(guint8 *data, size_t i, guint8 val);
/*! \brief Helper method to set two bytes at a memory position
* @param[in] data memory data pointer
* @param[in] i position in memory to change
* @param[in] val value to write
*/
void janus_set2(guint8 *data, size_t i, guint32 val);
/*! \brief Helper method to set three bytes at a memory position
* @param[in] data memory data pointer
* @param[in] i position in memory to change
* @param[in] val value to write
*/
void janus_set3(guint8 *data, size_t i, guint32 val);
/*! \brief Helper method to set four bytes at a memory position
* @param[in] data memory data pointer
* @param[in] i position in memory to change
* @param[in] val value to write
*/
void janus_set4(guint8 *data, size_t i, guint32 val);
/*! \brief Helper method to compress a string to gzip (using zlib)
* \note It's up to you to provide a buffer large enough for the compressed
* data: in case the buffer isn't large enough, the request will fail
* @param[in] compression Compression factor (1=fastest, 9=best compression)
* @param[in] text Pointer to the string to compress
* @param[in] tlen Length of the string to compress
* @param[in] compressed Pointer to the buffer where to compress the string to
* @param[in] zlen Size of the output buffer
* @returns The size of the compressed data, if successful, or 0 otherwise
*/
size_t janus_gzip_compress(int compression, char *text, size_t tlen, char *compressed, size_t zlen);
#endif