Statistics
| Branch: | Revision:

peerstreamer-src / src / janus_instance.c @ 07dd477d

History | View | Annotate | Download (17.9 KB)

1
/*******************************************************************
2
* PeerStreamer-ng is a P2P video streaming application exposing a ReST
3
* interface.
4
* Copyright (C) 2017 Luca Baldesi <luca.baldesi@unitn.it>
5
*
6
* This program is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU Affero General Public License as published by
8
* the Free Software Foundation, either version 3 of the License, or
9
* (at your option) any later version.
10
*
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
* GNU Affero General Public License for more details.
15
*
16
* You should have received a copy of the GNU Affero General Public License
17
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
*******************************************************************/
19

    
20
#include<janus_instance.h>
21
#include<signal.h>
22
#include<string.h>
23
#include<debug.h>
24
#include<unistd.h>
25
#include<tokens.h>
26
#include<name_lengths.h>
27
#include<grapes_config.h>
28
#include <sys/prctl.h>  // for process sync
29

    
30
#define INVALID_PID -1
31

    
32
#define JANUS_MSG_SESSION_CREATE "{\"transaction\": \"random\", \"janus\": \"create\"}"
33
#define JANUS_MSG_SESSION_KEEPALIVE "{\"transaction\": \"ciao\", \"janus\": \"keepalive\"}"
34
#define JANUS_MSG_STREAMING_PLUGIN_CREATE "{\"transaction\": \"ciao\", \"janus\": \"attach\", \"plugin\":\"janus.plugin.streaming\"}"
35
#define JANUS_MSG_VIDEOROOM_PLUGIN_CREATE "{\"transaction\": \"ciao\", \"janus\": \"attach\", \"plugin\":\"janus.plugin.videoroom\"}"
36

    
37

    
38
struct janus_instance {
39
        pid_t janus_pid;
40
        char* endpoint;
41
        char * executable;
42
        char * conf_param;
43
        char * logfile;
44
        struct mg_mgr *mongoose_srv;
45
        struct periodic_task * heartbeat;
46
        uint64_t management_session;
47
        uint64_t streaming_plugin_handle;
48
        uint64_t videoroom_plugin_handle;
49
        struct task_manager * tm;
50
};
51

    
52
uint64_t janus_instance_msg_get_id(char *msg)
53
{
54
        char ** records;
55
        uint32_t ntoks, i;
56
        uint64_t res = 0;
57
        
58
        records = tokens_create(msg, ' ', &ntoks);
59
        if ((i = tokens_check(records, ntoks, "\"id\":")) > 0)
60
        {
61
                if (records[i+1][strlen(records[i+1])-1] == ',')
62
                        records[i+1][strlen(records[i+1])-1] = '\0';
63
                sscanf(records[i+1], "%"PRId64"", &res);
64
                debug("ID string: %s\t ID integer: %"PRId64"\n", records[i+1], res);
65
        }
66
        tokens_destroy(&records, ntoks);
67
        return res;
68
}
69

    
70
char * janus_instance_streaming_handle_path(const struct janus_instance * janus)
71
{
72
        char * res = NULL;
73
        if (janus && janus->management_session && janus->streaming_plugin_handle && janus->endpoint)
74
        {
75
                res = malloc(sizeof(char) * (strlen(janus->endpoint) + 43));  // each identifier comprises of 20 characters at most
76
                sprintf(res, "%s/%"PRId64"/%"PRId64"", janus->endpoint, janus->management_session, janus->streaming_plugin_handle);
77
        }
78
        return res;
79
}
80

    
81
char * janus_instance_videoroom_handle_path(const struct janus_instance * janus)
82
{
83
        char * res = NULL;
84
        if (janus && janus->management_session && janus->videoroom_plugin_handle && janus->endpoint)
85
        {
86
                res = malloc(sizeof(char) * (strlen(janus->endpoint) + 43));  // each identifier comprises of 20 characters at most
87
                sprintf(res, "%s/%"PRId64"/%"PRId64"", janus->endpoint, janus->management_session, janus->videoroom_plugin_handle);
88
        }
89
        return res;
90
}
91

    
92
char * janus_instance_session_path(const struct janus_instance * janus)
93
{
94
        char * res = NULL;
95
        if (janus && janus->management_session && janus->endpoint)
96
        {
97
                res = malloc(sizeof(char) * (strlen(janus->endpoint) + 22));  // session identifier can be at most 20 characters long
98
                sprintf(res, "%s/%"PRId64"", janus->endpoint, janus->management_session);
99
        }
100
        return res;
101
}
102

    
103
struct janus_instance * janus_instance_create(struct mg_mgr *mongoose_srv, struct task_manager *tm, const char *config)
104
{
105
        struct janus_instance * ji = NULL;
106
        char * ptr = NULL;
107
        struct tag* tags;
108

    
109
        if (mongoose_srv && tm)
110
        {
111
                tags = grapes_config_parse(config);
112

    
113
                ji = malloc(sizeof(struct janus_instance));
114
                ji->endpoint = strdup(grapes_config_value_str_default(tags, "janus_endpoint", "127.0.0.1:8088/janus"));
115
                ji->executable = strdup(grapes_config_value_str_default(tags, "janus_executable", "Tools/janus/bin/janus"));
116
                ji->conf_param = strdup(grapes_config_value_str_default(tags, "janus_param", "--configs-folder:Tools/janus_conf"));
117
                while ((ptr = strchr(ji->conf_param, ':')))
118
                        *ptr='=';
119

    
120
                ji->logfile = strdup(grapes_config_value_str_default(tags, "janus_logfile", "janus.log"));
121
                ji->janus_pid = INVALID_PID;
122
                ji->management_session = 0;
123
                ji->streaming_plugin_handle = 0;
124
                ji->videoroom_plugin_handle = 0;
125
                ji->tm = tm;
126
                ji->heartbeat = NULL;
127
                ji->mongoose_srv = mongoose_srv;
128

    
129
                if(tags)
130
                        free(tags);
131
        }
132
        return ji;
133
}
134

    
135
void janus_instance_destroy(struct janus_instance ** ji)
136
{
137
        if (ji && (*ji))
138
        {
139
                if ((*ji)->janus_pid != INVALID_PID)
140
                        kill((*ji)->janus_pid, SIGHUP);
141
                        
142
                if ((*ji)->heartbeat)
143
                        task_manager_destroy_task((*ji)->tm, &((*ji)->heartbeat));
144
                free((*ji)->endpoint);
145
                free((*ji)->executable);
146
                free((*ji)->conf_param);
147
                free((*ji)->logfile);
148
                free(*ji);
149
                *ji = NULL;
150
        }
151
}
152

    
153
void janus_instance_generic_handler(struct mg_connection *nc, int ev, void *ev_data)
154
{
155
        struct http_message *hm = (struct http_message *) ev_data;
156

    
157
        switch (ev) {
158
                case MG_EV_CONNECT:
159
                        if (*(int *) ev_data != 0)
160
                                debug("Janus communication failure\n");
161
                        break;
162
                case MG_EV_HTTP_REPLY:
163
                        switch (hm->resp_code) {
164
                                case 200:
165
                                default:
166
                                        debug("Janus answers: %d\n", hm->resp_code);
167
                        }
168
                        nc->flags |= MG_F_SEND_AND_CLOSE;
169
                        break;
170
                case MG_EV_CLOSE:
171
                        debug("Janus server closed connection\n");
172
                        break;
173
                default:
174
                        break;
175
        }
176
}
177

    
178
void janus_instance_streaming_plugin_handler(struct mg_connection *nc, int ev, void *ev_data)
179
{
180
        struct janus_instance * janus;
181
        struct http_message *hm = (struct http_message *) ev_data;
182
        char *buff;
183

    
184
        janus = nc->user_data;
185
        switch (ev) {
186
                case MG_EV_CONNECT:
187
                        if (*(int *) ev_data != 0)
188
                                debug("Janus communication failure\n");
189
                        break;
190
                case MG_EV_HTTP_REPLY:
191
                        switch (hm->resp_code) {
192
                                case 200:
193
                                        buff = malloc(sizeof(char) * (hm->body.len + 1));
194
                                        strncpy(buff, hm->body.p, hm->body.len);
195
                                        buff[hm->body.len] = '\0';  // make sure string terminates
196
                                        janus->streaming_plugin_handle = janus_instance_msg_get_id(buff);
197
                                        free(buff);
198
                                        debug("Got plugin streaming_handle!\n");
199
                                default:
200
                                        debug("Janus answers: %d\n", hm->resp_code);
201
                        }
202
                        nc->flags |= MG_F_SEND_AND_CLOSE;
203
                        break;
204
                case MG_EV_CLOSE:
205
                        debug("Janus server closed connection\n");
206
                        break;
207
                default:
208
                        break;
209
        }
210
}
211

    
212
void janus_instance_videoroom_plugin_handler(struct mg_connection *nc, int ev, void *ev_data)
213
{
214
        struct janus_instance * janus;
215
        struct http_message *hm = (struct http_message *) ev_data;
216
        char *buff;
217

    
218
        janus = nc->user_data;
219
        switch (ev) {
220
                case MG_EV_CONNECT:
221
                        if (*(int *) ev_data != 0)
222
                                debug("Janus communication failure\n");
223
                        break;
224
                case MG_EV_HTTP_REPLY:
225
                        switch (hm->resp_code) {
226
                                case 200:
227
                                        buff = malloc(sizeof(char) * (hm->body.len + 1));
228
                                        strncpy(buff, hm->body.p, hm->body.len);
229
                                        buff[hm->body.len] = '\0';  // make sure string terminates
230
                                        janus->videoroom_plugin_handle = janus_instance_msg_get_id(buff);
231
                                        free(buff);
232
                                        debug("Got plugin videoroom_handle!\n");
233
                                default:
234
                                        debug("Janus answers: %d\n", hm->resp_code);
235
                        }
236
                        nc->flags |= MG_F_SEND_AND_CLOSE;
237
                        break;
238
                case MG_EV_CLOSE:
239
                        debug("Janus server closed connection\n");
240
                        break;
241
                default:
242
                        break;
243
        }
244
}
245

    
246
void janus_instance_session_handler(struct mg_connection *nc, int ev, void *ev_data)
247
{
248
        struct janus_instance * janus;
249
        struct mg_connection * conn;
250
        struct http_message *hm = (struct http_message *) ev_data;
251
        char *buff;
252

    
253
        janus = nc->user_data;
254
        switch (ev) {
255
                case MG_EV_CONNECT:
256
                        if (*(int *) ev_data != 0)
257
                                debug("Janus communication failure\n");
258
                        break;
259
                case MG_EV_HTTP_REPLY:
260
                        switch (hm->resp_code) {
261
                                case 200:
262
                                        buff = malloc(sizeof(char) * (hm->body.len + 1));
263
                                        strncpy(buff, hm->body.p, hm->body.len);
264
                                        buff[hm->body.len] = '\0';  // make sure string terminates
265
                                        janus->management_session = janus_instance_msg_get_id(buff);
266
                                        free(buff);
267
                                        
268
                                        // Requesting handle for the streaming plugin
269
                                        buff = malloc(sizeof(char) * (strlen(janus->endpoint) + 22));
270
                                        sprintf(buff, "%s/%"PRId64"", janus->endpoint, janus->management_session);
271
                                        conn = mg_connect_http(janus->mongoose_srv, janus_instance_streaming_plugin_handler, buff, NULL, JANUS_MSG_STREAMING_PLUGIN_CREATE);
272
                                        free(buff);
273
                                        if (conn)
274
                                                conn->user_data = (void *) janus;
275

    
276
                                        // Requesting handle for the videoroom plugin
277
                                        buff = malloc(sizeof(char) * (strlen(janus->endpoint) + 22));
278
                                        sprintf(buff, "%s/%"PRId64"", janus->endpoint, janus->management_session);
279
                                        conn = mg_connect_http(janus->mongoose_srv, janus_instance_videoroom_plugin_handler, buff, NULL, JANUS_MSG_VIDEOROOM_PLUGIN_CREATE);
280
                                        free(buff);
281
                                        if (conn)
282
                                                conn->user_data = (void *) janus;
283
                                default:
284
                                        debug("Janus answers: %d\n", hm->resp_code);
285
                        }
286
                        nc->flags |= MG_F_SEND_AND_CLOSE;
287
                        break;
288
                case MG_EV_CLOSE:
289
                        debug("Janus server closed connection\n");
290
                        break;
291
                default:
292
                        break;
293
        }
294
}
295

    
296
int8_t        janus_instance_create_management_handle(struct janus_instance *janus)
297
{
298
        struct mg_connection * conn;
299
        int8_t res = -1;
300

    
301
        if (janus)
302
        {
303
                conn = mg_connect_http(janus->mongoose_srv, janus_instance_session_handler, janus->endpoint, NULL, JANUS_MSG_SESSION_CREATE);
304
                if (conn)
305
                {
306
                        conn->user_data = (void *) janus;
307
                        res = 0;
308
                } 
309
        }
310
        return res;
311
}
312

    
313
uint8_t janus_instance_heartbeat(struct periodic_task * pt)
314
{
315
        struct janus_instance * janus;
316
        struct mg_connection * conn;
317
        char * uri;
318

    
319
        janus = (struct janus_instance *) periodic_task_get_data(pt);
320
        uri = janus_instance_session_path(janus);
321
        if (uri)
322
        {
323
                conn = mg_connect_http(janus->mongoose_srv, janus_instance_generic_handler, uri, NULL, JANUS_MSG_SESSION_KEEPALIVE);
324
                if (conn)
325
                        conn->user_data = (void *) janus;
326
                free(uri);
327
        }
328
        return 0;
329
}
330

    
331
int8_t janus_instance_launch(struct janus_instance * ji)
332
{
333
        int8_t res = -1;
334
        struct stat s;
335
        char * argv[4];
336
        int fd;
337

    
338
        if (ji && ji->janus_pid == INVALID_PID)
339
        {
340
                info("%s - %s\n", ji->executable, ji->conf_param);
341
                res = stat(ji->executable, &s);
342
                // check exe existence
343
                if (res == 0 && S_ISREG(s.st_mode))
344
                {
345
                        ji->janus_pid = fork();
346
                        if (ji->janus_pid != INVALID_PID)
347
                        {
348
                                if (ji->janus_pid) // the parent
349
                                {
350
                                        sleep(1); // let janus bootstrap
351
                                        res = janus_instance_create_management_handle(ji);
352
                                        if (res == 0)
353
                                                ji->heartbeat = task_manager_new_task(ji->tm, janus_instance_heartbeat, NULL, 30000, ji);
354
                                }
355
                                else // the child
356
                                {
357
                                        fd = creat(ji->logfile, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
358
                                        dup2(fd, 1);   // make stdout go to file
359
                                        dup2(fd, 2);   // make stderr go to file - you may choose to not do this
360
                                        close(fd);
361

    
362
                                        prctl(PR_SET_PDEATHSIG, SIGHUP); // makes kernel dispatch me a SIGHUP if parent dies
363

    
364
                                        argv[0] = ji->executable;
365
                                        argv[1] = ji->conf_param;
366
                                        argv[2] = NULL;
367
                                        res = execve(ji->executable, argv, NULL);
368
                                        info("Error on launching Janus execution\n");
369
                                }
370
                        } else
371
                        {
372
                                info("Error on forking\n");
373
                                res = -1;
374
                        }
375

    
376
                        
377
                } else
378
                        info("Janus executable not found\n");
379
        }
380
        return res;
381
}
382

    
383
void janus_instance_streaming_point_handler(struct mg_connection *nc, int ev, void *ev_data)
384
{
385
        uint64_t * mp_id;
386
        struct http_message *hm = (struct http_message *) ev_data;
387
        char *buff;
388
        void ** data;
389
        struct streamer_creation_callback * scc;
390

    
391
        data = nc->user_data;
392
        mp_id = data[0];
393
        scc = data[1];
394
        switch (ev) {
395
                case MG_EV_CONNECT:
396
                        if (*(int *) ev_data != 0)
397
                                debug("Janus communication failure\n");
398
                                debug("Ora triggero!\n");
399
                        break;
400
                case MG_EV_HTTP_REPLY:
401
                        switch (hm->resp_code) {
402
                                case 200:
403
                                        buff = malloc(sizeof(char) * (hm->body.len + 1));
404
                                        strncpy(buff, hm->body.p, hm->body.len);
405
                                        buff[hm->body.len] = '\0';  // make sure string terminates
406
                                        debug(buff);
407
                                        *mp_id = janus_instance_msg_get_id(buff);
408
                                        free(buff);
409
                                default:
410
                                        debug("Janus answers: %d\n", hm->resp_code);
411
                        }
412
                        nc->flags |= MG_F_SEND_AND_CLOSE;
413
                        break;
414
                case MG_EV_CLOSE:
415
                        debug("Janus server closed connection\n");
416
                        if (scc)
417
                        {
418
                                debug("Janus instance calls creation trigger\n");
419
                                streamer_creation_callback_trigger(scc, *mp_id ? 0 : 1);
420
                                streamer_creation_callback_destroy(&scc);
421
                                free(data);
422
                        }
423
                        break;
424
                default:
425
                        break;
426
        }
427
}
428

    
429
void janus_instance_videoroom_creation_handler(struct mg_connection *nc, int ev, void *ev_data)
430
{
431
        struct http_message *hm = (struct http_message *) ev_data;
432
        void ** data;
433
        struct streamer_creation_callback * scc;
434

    
435
        data = nc->user_data;
436
        scc = data[0];
437
        switch (ev) {
438
                case MG_EV_CONNECT:
439
                        if (*(int *) ev_data != 0)
440
                                debug("Janus communication failure\n");
441
                        break;
442
                case MG_EV_HTTP_REPLY:
443
                        switch (hm->resp_code) {
444
                                case 200:
445
                                        info("Room created\n");
446
                                default:
447
                                        debug("Janus answers: %d\n", hm->resp_code);
448
                        }
449
                        nc->flags |= MG_F_SEND_AND_CLOSE;
450
                        break;
451
                case MG_EV_CLOSE:
452
                        debug("Janus server closed connection\n");
453
                        if (scc)
454
                        {
455
                                debug("Janus instance calls creation trigger\n");
456
                                streamer_creation_callback_trigger(scc, 0);
457
                                streamer_creation_callback_destroy(&scc);
458
                                free(data);
459
                        }
460
                        break;
461
                default:
462
                        break;
463
        }
464
}
465

    
466
int8_t janus_instance_create_streaming_point(struct janus_instance const * janus, uint64_t *mp_id, uint16_t audio_port, uint16_t video_port, struct streamer_creation_callback *scc)
467
{
468
        struct mg_connection * conn;
469
        int8_t res = -1;
470
        char * uri;
471
        char * fmt = "{\"transaction\":\"random_str\",\"janus\":\"message\",\"body\":{\"request\":\"create\",\"type\":\"rtp\",\
472
                                  \"audio\":true,\"audioport\":%"PRId16",\"audiopt\":111,\"audiortpmap\":\"opus/48000/2\",\
473
                                  \"video\":true,\"videoport\":%"PRId16",\"videopt\":98,\"videortpmap\":\"VP8/90000\"}}";
474
        char buff[280];
475
        void ** data;
476

    
477
        if (janus && mp_id && audio_port && video_port)
478
        {
479
                uri = janus_instance_streaming_handle_path(janus);
480
                if (uri)
481
                {
482
                        sprintf(buff, fmt, audio_port, video_port);
483
                   debug("Conctating Janus to create a new mountpoint\n");        
484
                        conn = mg_connect_http(janus->mongoose_srv, janus_instance_streaming_point_handler, uri, NULL, buff);
485
                        if (conn)
486
                        {
487
                                data = malloc(sizeof(void *) * 2);
488
                                data[0] = mp_id;
489
                                data[1] = scc;
490
                                conn->user_data = data;
491
                                res = 0;
492
                        } else
493
                           debug("Aaargh, no connection!\n");        
494
                        free(uri);
495
                }
496
        }
497
        return res;
498
}
499

    
500
int8_t janus_instance_destroy_streaming_point(struct janus_instance const * janus, uint64_t mp_id)
501
{
502
        struct mg_connection * conn;
503
        int8_t res = -1;
504
        char * uri;
505
        char * fmt = "{\"transaction\":\"random_str\",\"janus\":\"message\",\"body\":{\"request\":\"destroy\",\"id\": %"PRId64"}}";
506
        char buff[120];
507

    
508
        if (janus && mp_id)
509
        {
510
                uri = janus_instance_streaming_handle_path(janus);
511
                if (uri)
512
                {
513
                        sprintf(buff, fmt, mp_id);
514
                        conn = mg_connect_http(janus->mongoose_srv, janus_instance_generic_handler, uri, NULL, buff);
515
                        if (conn)
516
                        {
517
                                conn->user_data = (void *) mp_id;
518
                                res = 0;
519
                        } 
520
                        free(uri);
521
                }
522
        }
523
        return res;
524
}
525

    
526
int8_t janus_instance_create_videoroom(struct janus_instance const * janus, const char * room_id, struct streamer_creation_callback *scc)
527
{
528
        struct mg_connection * conn;
529
        int8_t res = -1;
530
        char * uri;
531
        char * fmt = "{\"transaction\":\"random_str\",\"janus\":\"message\",\"body\":{\"request\":\"create\",\"room\":%s,\"publishers\":1,\"bitrate\":128000,\"record\":false,\"description\":\"Room %s\",\"fir_freq\":5,\"audiocodec\":\"opus\",\"videocodec\":\"vp8\"}}";
532

    
533
        char buff[280];
534
        void ** data;
535

    
536
        if (janus && room_id)
537
        {
538
                uri = janus_instance_videoroom_handle_path(janus);
539
                if (uri)
540
                {
541
                        sprintf(buff, fmt, room_id, room_id);
542
                   debug("Conctating Janus to create a new video room\n");        
543
                        conn = mg_connect_http(janus->mongoose_srv, janus_instance_videoroom_creation_handler, uri, NULL, buff);
544
                        if (conn)
545
                        {
546
                                data = malloc(sizeof(void *));
547
                                data[0] = scc;
548
                                conn->user_data = data;
549
                                res = 0;
550
                        } else
551
                           debug("Aaargh, no connection!\n");        
552
                        free(uri);
553
                }
554
        }
555
        return res;
556
}
557

    
558
int8_t janus_instance_destroy_videoroom(struct janus_instance const * janus, const char * room_id)
559
{
560
        struct mg_connection * conn;
561
        int8_t res = -1;
562
        char * uri;
563
        char * fmt = "{\"transaction\":\"random_str\",\"janus\":\"message\",\"body\":{\"request\":\"destroy\",\"room\": %s}}";
564
        char buff[120];
565

    
566
        if (janus && room_id)
567
        {
568
                uri = janus_instance_videoroom_handle_path(janus);
569
                if (uri)
570
                {
571
                        sprintf(buff, fmt, room_id);
572
                        conn = mg_connect_http(janus->mongoose_srv, janus_instance_generic_handler, uri, NULL, buff);
573
                        if (conn)
574
                        {
575
                                conn->user_data = (void *) room_id;
576
                                res = 0;
577
                        } 
578
                        free(uri);
579
                }
580
        }
581
        return res;
582
}
583

    
584
int8_t janus_instance_forward_rtp(struct janus_instance const * janus, const char * room_id, uint64_t participant_id, const char * rtp_dest, uint16_t audio_port, uint16_t video_port)
585
{
586
        struct mg_connection * conn;
587
        int8_t res = -1;
588
        char * uri;
589
        char * fmt = "{\"transaction\":\"random_str\",\"janus\":\"message\",\"body\":{\"request\":\"rtp_forward\",\"room\":%s,\"publisher_id\":%"PRId64", \"host\": \"%s\",\"audio_port\":%"PRId16",\"video_port\":%"PRId16",\"audio_pt\":111,\"video_pt\":98}}";
590

    
591
        char buff[280];
592

    
593
        if (janus && room_id && rtp_dest && audio_port > 0 && video_port > 0)
594
        {
595
                uri = janus_instance_videoroom_handle_path(janus);
596
                if (uri)
597
                {
598
                        sprintf(buff, fmt, room_id, participant_id, rtp_dest, audio_port, video_port);
599
                    debug("Conctating Janus to create a new video room\n");        
600
                        conn = mg_connect_http(janus->mongoose_srv, janus_instance_generic_handler, uri, NULL, buff);
601
                        if (conn)
602
                                res = 0;
603
                        else
604
                           debug("Aaargh, no connection!\n");        
605
                        free(uri);
606
                }
607
        }
608
        return res;
609
}
610

    
611
const char * janus_instance_ipaddr(const struct janus_instance * janus)
612
{
613
        static char res[MAX_IPADDR_LENGTH];
614
    char *ptr;
615
        res[0] = '\0';
616

    
617
        if (janus && janus->endpoint)
618
        {
619
                ptr = strchr(janus->endpoint, ':');
620
                if (ptr)
621
                {
622
                        strncpy(res, janus->endpoint, ptr - janus->endpoint);
623
                        res[janus->endpoint - ptr] = '\0';
624
                } else
625
                        strncpy(res, janus->endpoint, MAX_IPADDR_LENGTH);
626
        }
627
        return res;
628
}