Revision 5e9e29e0

View differences:

Makefile
1 1
CC = gcc
2
STUFF = $(shell pkg-config --cflags glib-2.0 nice libmicrohttpd jansson libssl libcrypto sofia-sip-ua ini_config) -ldl -D_GNU_SOURCE
3
LIBS = $(shell pkg-config --libs glib-2.0 nice libmicrohttpd jansson libssl libcrypto sofia-sip-ua ini_config) -ldl -lsrtp -D_GNU_SOURCE
2
STUFF = $(shell pkg-config --cflags glib-2.0 nice libmicrohttpd jansson libssl libcrypto sofia-sip-ua ini_config) -ldl -D_GNU_SOURCE $(HAVE_PORTRANGE)
3
LIBS = $(shell pkg-config --libs glib-2.0 nice libmicrohttpd jansson libssl libcrypto sofia-sip-ua ini_config) -ldl -lsrtp -D_GNU_SOURCE $(HAVE_PORTRANGE)
4 4
OPTS = -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wunused #-Werror #-O2
5 5
GDB = -g -ggdb #-gstabs
6
OBJS=janus.o cmdline.o config.o apierror.o rtcp.o dtls.o ice.o sdp.o
6
OBJS=janus.o cmdline.o config.o apierror.o rtcp.o dtls.o ice.o sdp.o utils.o
7 7

  
8 8
all: janus cmdline plugins
9 9

  
README
1
Janus WebRTC Gateway
2
====================
3

  
4
Janus is an open source, general purpose, WebRTC gateway. This version
5
of the gateway can only be installed on Linux systems: next versions
6
will take into account cross compilation on different environments.
7

  
8
For some online demos and documentations, make sure you pay the
9
[project website](http://janus.conf.meetecho.com/) a visit!
10

  
11
##Dependencies
12
To install it, you'll need to satisfy the following dependencies:
13

  
14
* [libmicrohttpd](http://www.gnu.org/software/libmicrohttpd/)
15
* [libini-config](https://fedorahosted.org/sssd/) (INI configurations)
16
* [Jansson](http://www.digip.org/jansson/)
17
* [libnice](http://nice.freedesktop.org/wiki/)
18
* [OpenSSL](http://www.openssl.org/) (at least v1.0.1e)
19
* [libsrtp](http://srtp.sourceforge.net/srtp.html)
20
* [Sofia-SIP](http://sofia-sip.sourceforge.net/)
21

  
22
A couple of plugins depend on a few more libraries:
23

  
24
* [libopus](http://opus-codec.org/) (only needed for the bridge plugin)
25
* [libogg](http://xiph.org/ogg/) (only needed for the voicemail plugin)
26

  
27
Additionally, you'll need the following libraries and tools:
28

  
29
* [GLib](http://library.gnome.org/devel/glib/)
30
* [pkg-config](http://www.freedesktop.org/wiki/Software/pkg-config/)
31
* [gengetopt](http://www.gnu.org/software/gengetopt/)
32

  
33
All of those libraries are usually available on most of the most common
34
distributions. Installing these libraries on a recent Fedora, for
35
instance, is very simple:
36

  
37
    yum install libmicrohttpd-devel jansson-devel libnice-devel \
38
       openssl-devel libsrtp-devel sofia-sip-devel glib-devel \
39
       opus-devel libogg-devel libini_config-devel pkg-config gengetopt
40

  
41
On Ubuntu or Debian, it would require something like this:
42

  
43
	aptitude install libmicrohttpd-dev libjansson-dev libnice-dev \
44
		libssl-dev libsrtp-dev libsofia-sip-ua-dev libglib2.0-dev \
45
		libogg-dev libini-config-dev libcollection-dev pkg-config gengetopt
46

  
47
* *Note:* apparently libopus is not available on Ubuntu: you'll have to
48
install it manually.
49

  
50
Should you be interested in building the gateway documentation as well,
51
you'll need an additional component installed too:
52

  
53
* [Doxygen](http://www.doxygen.org)
54
* [Graphviz](http://www.graphviz.org/)
55

  
56
On Fedora:
57

  
58
	yum install doxygen graphviz
59

  
60
On Ubuntu/Debian:
61

  
62
	aptitude install doxygen graphviz
63

  
64

  
65
##Compile
66
Once you have installed all the dependencies, just use:
67

  
68
	sh install.sh
69

  
70
to start the whole compilation process. The script will try to check
71
whether you have all the dependencies installed, and then issue a 'make'
72
for you to start compiling. If Doxygen and graphviz are available, it
73
will also build the documentation for you as well.
74

  
75
If you prefer doing this manually, a simple
76

  
77
	make
78

  
79
will start compiling the gateway. To build the documentation as well:
80

  
81
	make docs
82

  
83
will create the documentation in the docs/html subfolder.
84

  
85

  
86
##Configure and start
87
To start the gateway, you can use the janus executable. There are several
88
things you can configure, either in a configuration file:
89

  
90
	./conf/janus.cfg
91

  
92
or on the command line:
93

  
94
	./janus --help
95
	
96
	janus 0.0.1
97

  
98
	Usage: janus [OPTIONS]...
99

  
100
	-h, --help                    Print help and exit
101
	-V, --version                 Print version and exit
102
	-i, --interface=ipaddress     Interface to use (will be the public IP)
103
	-p, --port=portnumber         Web server HTTP port (default=8088)
104
	-s, --secure-port=portnumber  Web server HTTPS port (default=no HTTPS)
105
	-n, --no-http                 Disable insecure HTTP web server  (default=off)
106
	-b, --base-path=basepath      Base path to bind to in the web server 
107
								  (default=/janus) 
108
	-P, --plugins-folder=path     Plugins folder (default=./plugins)
109
	-C, --config=path             Configuration file to use
110
	-F, --configs-folder=path     Configuration files folder (default=./conf)
111
	-c, --cert-pem=filename       HTTPS/DTLS certificate
112
	-k, --cert-key=filename       HTTPS/DTLS certificate key
113
	-S, --stun-server=filename    STUN server(:port) to use, if needed (e.g., 
114
								  gateway behind NAT, default=none)
115
	-e, --public-ip=ipaddress     Public address of the machine, to use in SDP
116
	-r, --rtp-port-range=min-max  Port range to use for RTP/RTCP (only available
117
								  if the installed libnice supports it)
118

  
119
Options passed through the command line have the precedence on those
120
specified in the configuration file. To start the gateway, simply run:
121

  
122
	./janus
123

  
124
This will start the gateway, and have it look at the configuration file.
125
By default, only an HTTP webserver is started. To enable HTTPS support,
126
edit the configuration file accordingly or use the command line. The
127
webserver will make use of the same certificates provided for DTLS. You
128
can also change the base path that the webserver uses: by default this
129
is /janus, but you can change it to anything you want and with any nesting
130
you want (e.g., /mypath, /my/path, or /my/really/nested/path). This is
131
done to allow you to more easily customize rules in any frontend you
132
may have (e.g., Apache in front of your services).
133
 
134
In the absence of a configuration file, the only mandatory options to
135
specify in the command line are the ones related to the DTLS certificate.
136
A default certificate is provided with this package in the certs folder,
137
which you can use launching the executable with these parameters:
138

  
139
	./janus -c certs/mycert.pem -k certs/mycert.key
140

  
141
At this point, the gateway will be listening on the 8088 port (or whatever
142
you changed that to) of your machine. To test whether it's working
143
correctly, you can use the demos provided with this package in the html
144
folder: these are exactly the same demos available online on the
145
[project website](http://janus.conf.meetecho.com/). Just copy the file
146
it contains in a webserver, and open the index.html page in either
147
Chrome or Firefox. A list of demo pages exploiting the different plugins
148
will be available.
149

  
150

  
151
##Help us!
152
Any thought, feedback or (hopefully not!) insult is welcome!
153

  
154
Developed by [@meetecho](https://github.com/meetecho)
cmdline.c
44 44
  "  -F, --configs-folder=path     Configuration files folder (default=./conf)",
45 45
  "  -c, --cert-pem=filename       HTTPS/DTLS certificate",
46 46
  "  -k, --cert-key=filename       HTTPS/DTLS certificate key",
47
  "  -S, --stun-server=filename    STUN server(:port) to use, if needed (e.g., \n                                  gateway behind NAT, default=none)",
47
  "  -S, --stun-server=ip:port     STUN server(:port) to use, if needed (e.g., \n                                  gateway behind NAT, default=none)",
48
  "  -e, --public-ip=ipaddress     Public address of the machine, to use in SDP",
49
  "  -r, --rtp-port-range=min-max  Port range to use for RTP/RTCP",
48 50
    0
49 51
};
50 52

  
......
83 85
  args_info->cert_pem_given = 0 ;
84 86
  args_info->cert_key_given = 0 ;
85 87
  args_info->stun_server_given = 0 ;
88
  args_info->public_ip_given = 0 ;
89
  args_info->rtp_port_range_given = 0 ;
86 90
}
87 91

  
88 92
static
......
108 112
  args_info->cert_key_orig = NULL;
109 113
  args_info->stun_server_arg = NULL;
110 114
  args_info->stun_server_orig = NULL;
115
  args_info->public_ip_arg = NULL;
116
  args_info->public_ip_orig = NULL;
117
  args_info->rtp_port_range_arg = NULL;
118
  args_info->rtp_port_range_orig = NULL;
111 119
  
112 120
}
113 121

  
......
129 137
  args_info->cert_pem_help = gengetopt_args_info_help[10] ;
130 138
  args_info->cert_key_help = gengetopt_args_info_help[11] ;
131 139
  args_info->stun_server_help = gengetopt_args_info_help[12] ;
140
  args_info->public_ip_help = gengetopt_args_info_help[13] ;
141
  args_info->rtp_port_range_help = gengetopt_args_info_help[14] ;
132 142
  
133 143
}
134 144

  
......
227 237
  free_string_field (&(args_info->cert_key_orig));
228 238
  free_string_field (&(args_info->stun_server_arg));
229 239
  free_string_field (&(args_info->stun_server_orig));
240
  free_string_field (&(args_info->public_ip_arg));
241
  free_string_field (&(args_info->public_ip_orig));
242
  free_string_field (&(args_info->rtp_port_range_arg));
243
  free_string_field (&(args_info->rtp_port_range_orig));
230 244
  
231 245
  
232 246

  
......
283 297
    write_into_file(outfile, "cert-key", args_info->cert_key_orig, 0);
284 298
  if (args_info->stun_server_given)
285 299
    write_into_file(outfile, "stun-server", args_info->stun_server_orig, 0);
300
  if (args_info->public_ip_given)
301
    write_into_file(outfile, "public-ip", args_info->public_ip_orig, 0);
302
  if (args_info->rtp_port_range_given)
303
    write_into_file(outfile, "rtp-port-range", args_info->rtp_port_range_orig, 0);
286 304
  
287 305

  
288 306
  i = EXIT_SUCCESS;
......
550 568
        { "cert-pem",	1, NULL, 'c' },
551 569
        { "cert-key",	1, NULL, 'k' },
552 570
        { "stun-server",	1, NULL, 'S' },
571
        { "public-ip",	1, NULL, 'e' },
572
        { "rtp-port-range",	1, NULL, 'r' },
553 573
        { 0,  0, 0, 0 }
554 574
      };
555 575

  
556
      c = getopt_long (argc, argv, "hVi:p:s:nb:P:C:F:c:k:S:", long_options, &option_index);
576
      c = getopt_long (argc, argv, "hVi:p:s:nb:P:C:F:c:k:S:e:r:", long_options, &option_index);
557 577

  
558 578
      if (c == -1) break;	/* Exit from `while (1)' loop.  */
559 579

  
......
699 719
            goto failure;
700 720
        
701 721
          break;
722
        case 'e':	/* Public address of the machine, to use in SDP.  */
723
        
724
        
725
          if (update_arg( (void *)&(args_info->public_ip_arg), 
726
               &(args_info->public_ip_orig), &(args_info->public_ip_given),
727
              &(local_args_info.public_ip_given), optarg, 0, 0, ARG_STRING,
728
              check_ambiguity, override, 0, 0,
729
              "public-ip", 'e',
730
              additional_error))
731
            goto failure;
732
        
733
          break;
734
        case 'r':	/* Port range to use for RTP/RTCP.  */
735
        
736
        
737
          if (update_arg( (void *)&(args_info->rtp_port_range_arg), 
738
               &(args_info->rtp_port_range_orig), &(args_info->rtp_port_range_given),
739
              &(local_args_info.rtp_port_range_given), optarg, 0, 0, ARG_STRING,
740
              check_ambiguity, override, 0, 0,
741
              "rtp-port-range", 'r',
742
              additional_error))
743
            goto failure;
744
        
745
          break;
702 746

  
703 747
        case 0:	/* Long option with no short option */
704 748
        case '?':	/* Invalid option.  */
cmdline.h
71 71
  char * stun_server_arg;	/**< @brief STUN server(:port) to use, if needed (e.g., gateway behind NAT, default=none).  */
72 72
  char * stun_server_orig;	/**< @brief STUN server(:port) to use, if needed (e.g., gateway behind NAT, default=none) original value given at command line.  */
73 73
  const char *stun_server_help; /**< @brief STUN server(:port) to use, if needed (e.g., gateway behind NAT, default=none) help description.  */
74
  char * public_ip_arg;	/**< @brief Public address of the machine, to use in SDP.  */
75
  char * public_ip_orig;	/**< @brief Public address of the machine, to use in SDP original value given at command line.  */
76
  const char *public_ip_help; /**< @brief Public address of the machine, to use in SDP help description.  */
77
  char * rtp_port_range_arg;	/**< @brief Port range to use for RTP/RTCP.  */
78
  char * rtp_port_range_orig;	/**< @brief Port range to use for RTP/RTCP original value given at command line.  */
79
  const char *rtp_port_range_help; /**< @brief Port range to use for RTP/RTCP help description.  */
74 80
  
75 81
  unsigned int help_given ;	/**< @brief Whether help was given.  */
76 82
  unsigned int version_given ;	/**< @brief Whether version was given.  */
......
85 91
  unsigned int cert_pem_given ;	/**< @brief Whether cert-pem was given.  */
86 92
  unsigned int cert_key_given ;	/**< @brief Whether cert-key was given.  */
87 93
  unsigned int stun_server_given ;	/**< @brief Whether stun-server was given.  */
94
  unsigned int public_ip_given ;	/**< @brief Whether public-ip was given.  */
95
  unsigned int rtp_port_range_given ;	/**< @brief Whether rtp-port-range was given.  */
88 96

  
89 97
} ;
90 98

  
conf/janus.cfg
3 3
[general]
4 4
configs_folder = ./conf		; Configuration files folder
5 5
plugins_folder = ./plugins	; Plugins folder
6
;interface = 1.2.3.4		; Interface to use (will be the public IP)
6
;interface = 1.2.3.4		; Interface to use (will be used in SDP)
7 7

  
8 8
; Web server stuff: whether HTTP or HTTPS need to be enabled, on which
9 9
;ports, and what should be the base path for the Janus API protocol.
......
19 19
cert_pem = certs/mycert.pem
20 20
cert_key = certs/mycert.key
21 21

  
22
; NAT-related stuff: specifically, the STUN server to use to gather
23
; candidates if the gateway is behind a NAT, and srflx candidates are
24
; needed. By default, this section is commented as the gateway is
25
; assumed to be deployed on a public network.
22
; Media-related stuff: right now, you can only configure the range of
23
; ports to use for RTP and RTCP (by default, no range is envisaged).
24
; If you configure a range in the lines below, remember to uncomment the
25
; [media] category as well!
26
;[media]
27
;rtp_port_range = 20000-40000
28

  
29
; NAT-related stuff: specifically, you can either manually specify the 
30
; public IP of the machine (i.e., what would be used in SDP) or
31
; configure the STUN server to use to gather candidates if the gateway
32
; is behind a NAT, and srflx candidates are needed. By default, this
33
; section is commented as the gateway is assumed to be deployed on a
34
; public network. When configuring the public IP or the STUN
35
; configuration, remember to uncomment the [nat] categoty line as well!
26 36
;[nat]
37
;public_ip = 1.2.3.4
27 38
;stun_server = stun.voip.eutelia.it
28 39
;stun_port = 3478
conf/janus.plugin.videoroom.cfg
3 3
; publishers = <max number of concurrent senders> (e.g., 6 for a video
4 4
;              conference or 1 for a webinar)
5 5
; bitrate = <max video bitrate for senders> (e.g., 128000)
6
; fir_freq = <send a FIR to publishers every fir_freq seconds> (0=disable)
6 7

  
7 8
[1234]
8 9
description = Demo Room
9 10
publishers = 6
10 11
bitrate = 128000
12
fir_freq = 10
config.c
1
/*! \file    config.c
2
 * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3
 * \brief    Configuration files parsing
4
 * \details  Implementation of a parser of INI configuration files (based on libini-config).
5
 * 
6
 * \ingroup core
7
 * \ref core
8
 */
9

  
1 10
#include <stdio.h>
2 11
#include <stdlib.h>
3 12
#include <string.h>
config.h
1 1
/*! \file    config.h
2 2
 * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3
 * \brief    Configuration files parsing
3
 * \brief    Configuration files parsing (headers)
4 4
 * \details  Implementation of a parser of INI configuration files (based on libini-config).
5 5
 * 
6 6
 * \ingroup core
html/janus.js
484 484
			Janus.log(stream);
485 485
		config.myStream = stream;
486 486
		Janus.log("streamsDone:");
487
		Janus.log(stream);
487
		if(stream !== null && stream !== undefined)
488
			Janus.log(stream);
488 489
		var pc_config = {"iceServers": iceServers};
489 490
		//~ var pc_constraints = {'mandatory': {'MozDontOfferDataChannel':true}};
490 491
		var pc_constraints = {"optional": [{"DtlsSrtpKeyAgreement": true}]};
ice.c
16 16
 
17 17
#include <sys/socket.h>
18 18
#include <netdb.h>
19
#include <stun/usages/bind.h>
19 20

  
20 21
#include "janus.h"
21 22
#include "debug.h"
......
38 39
}
39 40

  
40 41

  
42
/* RTP/RTCP port range */
43
uint16_t rtp_range_min = 0;
44
uint16_t rtp_range_max = 0;
45

  
46

  
41 47
/* libnice initialization */
42
gint janus_ice_init(gchar *stun_server, uint16_t stun_port) {
48
gint janus_ice_init(gchar *stun_server, uint16_t stun_port, uint16_t rtp_min_port, uint16_t rtp_max_port) {
43 49
	if(stun_server == NULL)
44 50
		return 0;	/* No initialization needed */
45 51
	if(stun_port == 0)
46 52
		stun_port = 3478;
53
	/*! \todo The RTP/RTCP port range configuration may be just a placeholder: for
54
	 * instance, libnice supports this since 0.1.0, but the 0.1.3 on Fedora fails
55
	 * when linking with an undefined reference to \c nice_agent_set_port_range 
56
	 * so this is checked by the install.sh script in advance. */
57
	rtp_range_min = rtp_min_port;
58
	rtp_range_max = rtp_max_port;
59
#ifndef HAVE_PORTRANGE
60
	JANUS_DEBUG("nice_agent_set_port_range unavailable, port range disabled\n");
61
#endif
47 62
	JANUS_PRINT("STUN server to use: %s:%u\n", stun_server, stun_port);
48 63
	/* Resolve address to get an IP */
49 64
	struct hostent *he = gethostbyname(stun_server);
......
63 78
	}
64 79
	janus_stun_port = stun_port;
65 80
	JANUS_PRINT("  >> %s:%u\n", janus_stun_server, janus_stun_port);
66
	return 0;
81
	/* Test the STUN server */
82
	StunAgent stun;
83
	stun_agent_init (&stun, STUN_ALL_KNOWN_ATTRIBUTES, STUN_COMPATIBILITY_RFC5389, 0);
84
	StunMessage msg;
85
	uint8_t buf[1500];
86
	size_t len = stun_usage_bind_create(&stun, &msg, buf, 1500);
87
	JANUS_PRINT("Testing STUN server: message is of %zu bytes\n", len);
88
	int fd = socket(AF_INET, SOCK_DGRAM, 0);
89
	int yes = 1;	/* For setsockopt() SO_REUSEADDR */
90
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
91
	struct sockaddr_in address, remote;
92
	address.sin_family = AF_INET;
93
	address.sin_port = 0;
94
	address.sin_addr.s_addr = INADDR_ANY;
95
	remote.sin_family = AF_INET;
96
	remote.sin_port = htons(janus_stun_port);
97
	remote.sin_addr.s_addr = inet_addr(janus_stun_server);
98
	if(bind(fd, (struct sockaddr *)(&address), sizeof(struct sockaddr)) < 0) {
99
		JANUS_PRINT("Bind failed for STUN BINDING test\n");
100
		return -1;
101
	}
102
	int bytes = sendto(fd, buf, len, 0, (struct sockaddr*)&remote, sizeof(remote));
103
	if(bytes < 0) {
104
		JANUS_PRINT("Error sending STUN BINDING test\n");
105
		return -1;
106
	}
107
	JANUS_PRINT("  >> Sent %d bytes %s:%u, waiting for reply...\n", bytes, janus_stun_server, janus_stun_port);
108
	struct timeval timeout;
109
	fd_set readfds;
110
	FD_SET(fd, &readfds);
111
	timeout.tv_sec = 5;	/* FIXME Don't wait forever */
112
	timeout.tv_usec = 0;
113
	select(fd+1, &readfds, NULL, NULL, &timeout);
114
	if(!FD_ISSET(fd, &readfds)) {
115
		JANUS_PRINT("No response to our STUN BINDING test\n");
116
		return -1;
117
	}
118
	socklen_t addrlen = sizeof(remote);
119
	bytes = recvfrom(fd, buf, 1500, 0, (struct sockaddr*)&remote, &addrlen);
120
	JANUS_PRINT("  >> Got %d bytes...\n", bytes);
121
	if(stun_agent_validate (&stun, &msg, buf, bytes, NULL, NULL) < 0) {
122
		JANUS_PRINT("Failed to validate STUN BINDING response\n");
123
		return -1;
124
	}
125
	StunClass class = stun_message_get_class(&msg);
126
	StunMethod method = stun_message_get_method(&msg);
127
	if(class != STUN_RESPONSE || method != STUN_BINDING) {
128
		JANUS_PRINT("Unexpected STUN response: %d/%d\n", class, method);
129
		return -1;
130
	}
131
	StunMessageReturn ret = stun_message_find_xor_addr(&msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, (struct sockaddr *)&address, &addrlen);
132
	JANUS_PRINT("  >> XOR-MAPPED-ADDRESS: %d\n", ret);
133
	if(ret == STUN_MESSAGE_RETURN_SUCCESS) {
134
		janus_set_public_ip(inet_ntoa(address.sin_addr));
135
		JANUS_PRINT("  >> Our public address is %s\n", janus_get_public_ip());
136
		return 0;
137
	}
138
	ret = stun_message_find_addr(&msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, (struct sockaddr *)&address, &addrlen);
139
	JANUS_PRINT("  >> MAPPED-ADDRESS: %d\n", ret);
140
	if(ret == STUN_MESSAGE_RETURN_SUCCESS) {
141
		janus_set_public_ip(inet_ntoa(address.sin_addr));
142
		JANUS_PRINT("  >> Our public address is %s\n", janus_get_public_ip());
143
		return 0;
144
	}
145
	return -1;
67 146
}
68 147

  
69 148
/* ICE stuff */
......
502 581
	handle->icectx = g_main_context_new();
503 582
	handle->iceloop = g_main_loop_new(handle->icectx, FALSE);
504 583
	handle->icethread = g_thread_new("ice thread", &janus_ice_thread, handle);
505
	handle->agent = nice_agent_new(handle->icectx, NICE_COMPATIBILITY_RFC5245);
584
	/* Note: NICE_COMPATIBILITY_RFC5245 is only available in more recent versions of libnice */
585
	handle->agent = nice_agent_new(handle->icectx, NICE_COMPATIBILITY_DRAFT19);
506 586
	/* Any STUN server to use? */
507 587
	if(janus_stun_server != NULL && janus_stun_port > 0) {
508 588
		g_object_set (G_OBJECT(handle->agent),
......
566 646
		audio_stream->rtp_component = audio_rtp;
567 647
		g_hash_table_insert(audio_stream->components, GUINT_TO_POINTER(2), audio_rtcp);
568 648
		audio_stream->rtcp_component = audio_rtcp;
649
#ifdef HAVE_PORTRANGE
650
		/* FIXME: libnice supports this since 0.1.0, but the 0.1.3 on Fedora fails with an undefined reference! */
651
		nice_agent_set_port_range(handle->agent, handle->audio_id, 1, rtp_range_min, rtp_range_max);
652
		nice_agent_set_port_range(handle->agent, handle->audio_id, 2, rtp_range_min, rtp_range_max);
653
#endif
569 654
		nice_agent_gather_candidates (handle->agent, handle->audio_id);
570 655
		nice_agent_attach_recv (handle->agent, handle->audio_id, 1, g_main_loop_get_context (handle->iceloop), janus_ice_cb_nice_recv, audio_rtp);
571 656
		nice_agent_attach_recv (handle->agent, handle->audio_id, 2, g_main_loop_get_context (handle->iceloop), janus_ice_cb_nice_recv, audio_rtcp);
......
611 696
		video_stream->rtp_component = video_rtp;
612 697
		g_hash_table_insert(video_stream->components, GUINT_TO_POINTER(2), video_rtcp);
613 698
		video_stream->rtcp_component = video_rtcp;
699
#ifdef HAVE_PORTRANGE
700
		/* FIXME: libnice supports this since 0.1.0, but the 0.1.3 on Fedora fails with an undefined reference! */
701
		nice_agent_set_port_range(handle->agent, handle->video_id, 1, rtp_range_min, rtp_range_max);
702
		nice_agent_set_port_range(handle->agent, handle->video_id, 2, rtp_range_min, rtp_range_max);
703
#endif
614 704
		nice_agent_gather_candidates (handle->agent, handle->video_id);
615 705
		nice_agent_attach_recv (handle->agent, handle->video_id, 1, g_main_loop_get_context (handle->iceloop), janus_ice_cb_nice_recv, video_rtp);
616 706
		nice_agent_attach_recv (handle->agent, handle->video_id, 2, g_main_loop_get_context (handle->iceloop), janus_ice_cb_nice_recv, video_rtcp);
ice.h
26 26
/*! \brief ICE stuff initialization
27 27
 * @param[in] stun_server STUN server address to use, if any
28 28
 * @param[in] stun_port STUN port to use, if any
29
 * @param[in] rtp_min_port Minimum port to use for RTP/RTCP, if a range is to be used
30
 * @param[in] rtp_max_port Maximum port to use for RTP/RTCP, if a range is to be used
29 31
 * @returns 0 in case of success, a negative integer on errors */
30
gint janus_ice_init(gchar *stun_server, uint16_t stun_port);
32
gint janus_ice_init(gchar *stun_server, uint16_t stun_port, uint16_t rtp_min_port, uint16_t rtp_max_port);
31 33
/*! \brief Method to get the STUN server IP address
32 34
 * @returns The currently used STUN server IP address, if available, or NULL if not */
33 35
char *janus_ice_get_stun_server(void);
install.sh
23 23

  
24 24
echo
25 25
echo "Checking dependencies..."
26
for x in glib-2.0 nice libmicrohttpd jansson libssl libcrypto sofia-sip-ua ini_config opus ogg
26
for x in glib-2.0 nice libmicrohttpd jansson libssl libcrypto sofia-sip-ua ini_config
27 27
do
28 28
	DEPENDENCY=`pkg-config --cflags --libs $x`
29 29
	if [ -z "$DEPENDENCY" ]
......
32 32
		exit 1;
33 33
	fi
34 34
done
35
pkg-config --atleast-version=2.32 glib-2.0
36
if [ $? != 0 ]
37
then
38
	echo "  -- The installed glib-2.0 version (`pkg-config --modversion glib-2.0`) is outdated, at least 2.32 is required"
39
	exit 1
40
fi
41
pkg-config --atleast-version=1.0.1e openssl
42
if [ $? != 0 ]
43
then
44
	echo "  -- The installed openssl version (`pkg-config --modversion openssl`) is outdated, at least 1.0.1e is required"
45
	exit 1
46
fi
47
pkg-config --exists opus
48
if [ $? != 0 ]
49
then
50
	echo "  -- opus is not installed, the AudioBridge plugin will not be built"
51
	sleep 2
52
else
53
	export HAVE_OPUS=1
54
fi
55
pkg-config --exists ogg
56
if [ $? != 0 ]
57
then
58
	echo "  -- libogg is not installed, the VoiceMail plugin will not be built"
59
	sleep 2
60
else
61
	export HAVE_OGG=1
62
fi
63
LIBNICE=( `ldconfig -p | grep libnice.so | tail -n 1` )
64
PORTRANGE=`nm -AD ${LIBNICE[3]} | grep nice_agent_set_port_range`
65
if [ -z "$PORTRANGE" ]
66
then
67
	echo "  -- Your version of libnice does not have nice_agent_set_port_range: support for configuring an RTP/RTCP range will be disabled"
68
	sleep 2
69
else
70
	export HAVE_PORTRANGE="-DHAVE_PORTRANGE"
71
fi
35 72

  
36 73
echo
37 74
echo "Compiling..."
janus.c
27 27
#include "apierror.h"
28 28
#include "rtcp.h"
29 29
#include "sdp.h"
30
#include "utils.h"
30 31

  
31 32

  
32 33
static janus_config *config = NULL;
......
55 56
gchar *janus_get_local_ip() {
56 57
	return local_ip;
57 58
}
59
static gchar *public_ip = NULL;
60
gchar *janus_get_public_ip() {
61
	/* Fallback to the local IP, if we have no public one */
62
	return public_ip ? public_ip : local_ip;
63
}
64
void janus_set_public_ip(const char *ip) {
65
	if(ip == NULL)
66
		return;
67
	if(public_ip != NULL)
68
		g_free(public_ip);
69
	public_ip = g_strdup(ip);
70
}
58 71
static gint stop = 0;
59 72
gint janus_is_stopping() {
60 73
	return stop;
......
754 767
		MHD_destroy_response(response);
755 768
		return ret;
756 769
	}
757
	gint64 start = g_get_monotonic_time();
770
	gint64 start = janus_get_monotonic_time();
758 771
	gint64 end = 0;
759 772
	/* We have a timeout for the long poll: 30 seconds */
760 773
	while(end-start < 30*G_USEC_PER_SEC) {
......
765 778
		}
766 779
		/* Sleep 100ms */
767 780
		g_usleep(100000);
768
		end = g_get_monotonic_time();
781
		end = janus_get_monotonic_time();
769 782
	}
770 783
	if(event == NULL || event->payload == NULL) {
771 784
		JANUS_PRINT("Long poll time out for session %"SCNu64"...\n", session_id);
......
1171 1184
			janus_config_add_item(config, "nat", "stun_port", "3478");
1172 1185
		}
1173 1186
	}
1187
	if(args_info.public_ip_given) {
1188
		janus_config_add_item(config, "nat", "public_ip", args_info.public_ip_arg);
1189
	}
1190
	if(args_info.rtp_port_range_given) {
1191
		janus_config_add_item(config, "media", "rtp_port_range", args_info.rtp_port_range_arg);
1192
	}
1174 1193
	janus_config_print(config);
1175 1194

  
1176 1195
	/* What is the local public IP? */
......
1180 1199
		JANUS_PRINT("  -- Will try to use %s\n", item->value);
1181 1200
	struct ifaddrs *myaddrs, *ifa;
1182 1201
	int status = getifaddrs(&myaddrs);
1202
	char *tmp = NULL;
1183 1203
	if (status == 0) {
1184 1204
		for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) {
1185 1205
			if(ifa->ifa_addr == NULL) {
......
1202 1222
							exit(1);
1203 1223
						}
1204 1224
					} else if(strcasecmp(buf, "127.0.0.1")) {	/* FIXME Check private IP addresses as well */
1205
						local_ip = strdup(buf);
1206
						if(local_ip == NULL) {
1207
							JANUS_DEBUG("Memory error!\n");
1208
							exit(1);
1209
						}
1225
						if(tmp == NULL)	/* FIXME Take note of the first IP we find, we'll use it as a backup */
1226
							tmp = strdup(buf);
1210 1227
					}
1211 1228
				}
1212 1229
			}
......
1215 1232
		freeifaddrs(myaddrs);
1216 1233
	}
1217 1234
	if(local_ip == NULL) {
1218
		JANUS_DEBUG("Couldn't find any address! using 127.0.0.1 as local IP... (which is NOT going to work out of your machine)\n");
1219
		local_ip = "127.0.0.1";
1220
	} else {
1221
		JANUS_PRINT("Using %s as local IP...\n", local_ip);
1235
		if(tmp != NULL) {
1236
			local_ip = tmp;
1237
		} else {
1238
			JANUS_DEBUG("Couldn't find any address! using 127.0.0.1 as local IP... (which is NOT going to work out of your machine)\n");
1239
			local_ip = g_strdup("127.0.0.1");
1240
		}
1222 1241
	}
1242
	JANUS_PRINT("Using %s as local IP...\n", local_ip);
1223 1243

  
1224 1244
	/* Pre-parse the web server path, if any */
1225 1245
	ws_path = "/janus";
......
1239 1259
	/* Setup ICE stuff (e.g., checking if the provided STUN server is correct) */
1240 1260
	char *stun_server = NULL;
1241 1261
	uint16_t stun_port = 0;
1262
	uint16_t rtp_min_port = 0, rtp_max_port = 0;
1263
	item = janus_config_get_item_drilldown(config, "media", "rtp_port_range");
1264
	if(item && item->value) {
1265
		/* Split in min and max port */
1266
		char *maxport = strrchr(item->value, '-');
1267
		if(maxport != NULL) {
1268
			*maxport = '\0';
1269
			maxport++;
1270
			rtp_min_port = atoi(item->value);
1271
			rtp_max_port = atoi(maxport);
1272
			maxport--;
1273
			maxport = '-';
1274
		}
1275
		if(rtp_min_port > rtp_max_port) {
1276
			int temp_port = rtp_min_port;
1277
			rtp_min_port = rtp_max_port;
1278
			rtp_max_port = temp_port;
1279
		}
1280
		if(rtp_max_port == 0)
1281
			rtp_max_port = 65535;
1282
		JANUS_PRINT("RTP port range: %u -- %u\n", rtp_min_port, rtp_max_port);
1283
	}
1242 1284
	item = janus_config_get_item_drilldown(config, "nat", "stun_server");
1243 1285
	if(item && item->value)
1244 1286
		stun_server = (char *)item->value;
1245 1287
	item = janus_config_get_item_drilldown(config, "nat", "stun_port");
1246 1288
	if(item && item->value)
1247 1289
		stun_port = atoi(item->value);
1248
	if(janus_ice_init(stun_server, stun_port) < 0) {
1290
	if(janus_ice_init(stun_server, stun_port, rtp_min_port, rtp_max_port) < 0) {
1249 1291
		JANUS_DEBUG("Invalid STUN address %s:%u\n", stun_server, stun_port);
1250 1292
		exit(1);
1251 1293
	}
1294

  
1295
	/* Is there a public_ip value to be used for NAT traversal instead? */
1296
	item = janus_config_get_item_drilldown(config, "nat", "public_ip");
1297
	if(item && item->value) {
1298
		if(public_ip != NULL)
1299
			g_free(public_ip);
1300
		public_ip = g_strdup((char *)item->value);
1301
		if(public_ip == NULL) {
1302
			JANUS_DEBUG("Memory error\n");
1303
			exit(1);
1304
		}
1305
		JANUS_PRINT("Using %s as our public IP in SDP\n", public_ip);
1306
	}
1252 1307
	
1253 1308
	/* Setup OpenSSL stuff */
1254 1309
	item = janus_config_get_item_drilldown(config, "certificates", "cert_pem");
janus.ggo
8 8
option "configs-folder" F "Configuration files folder (default=./conf)" string typestr="path" optional
9 9
option "cert-pem" c "HTTPS/DTLS certificate" string typestr="filename" optional
10 10
option "cert-key" k "HTTPS/DTLS certificate key" string typestr="filename" optional
11
option "stun-server" S "STUN server(:port) to use, if needed (e.g., gateway behind NAT, default=none)" string typestr="filename" optional
11
option "stun-server" S "STUN server(:port) to use, if needed (e.g., gateway behind NAT, default=none)" string typestr="ip:port" optional
12
option "public-ip" e "Public address of the machine, to use in SDP" string typestr="ipaddress" optional
13
option "rtp-port-range" r "Port range to use for RTP/RTCP" string typestr="min-max" optional
janus.h
149 149
gchar *janus_get_server_key(void);
150 150

  
151 151

  
152
/*! \brief Helper method to return the local IP address */
152
/*! \brief Helper method to return the local IP address (autodetected by default) */
153 153
gchar *janus_get_local_ip(void);
154
/*! \brief Helper method to return the IP address to use in the SDP (autodetected by default) */
155
gchar *janus_get_public_ip(void);
156
/*! \brief Helper method to overwrite the IP address to use in the SDP */
157
void janus_set_public_ip(const char *ip);
154 158
/*! \brief Helper method to check whether the gateway is being shut down */
155 159
gint janus_is_stopping(void);
156 160

  
plugins/Makefile
3 3
LIBS = $(shell pkg-config --libs glib-2.0 jansson sofia-sip-ua opus ogg ini_config) -ldl -D_GNU_SOURCE
4 4
OPTS = -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wunused #-Werror #-O2
5 5
GDB = -g -ggdb #-gstabs
6
OBJS = janus_echotest.so janus_streaming.so janus_sip.so janus_videocall.so janus_videoroom.so
7
ifeq ($(HAVE_OPUS),1)
8
	OBJS += janus_audiobridge.so
9
endif
10
ifeq ($(HAVE_OGG),1)
11
	OBJS += janus_voicemail.so
12
endif
6 13

  
7
all: janus_echotest.so janus_streaming.so janus_sip.so janus_videocall.so janus_audiobridge.so janus_videoroom.so janus_voicemail.so
14
all: $(OBJS)
8 15

  
9 16
%.o: %.c
10 17
	$(CC) $(STUFF) -shared -fPIC $(GDB) -c $< -o $@ $(OPTS)
11 18

  
12
%.so: %.o ../rtcp.o
13
	$(CC) -shared -fPIC $(GDB) -o $@ $< ../config.o ../rtcp.o $(LIBS)
19
%.so: %.o ../config.o ../rtcp.o ../utils.o
20
	$(CC) -shared -fPIC $(GDB) -o $@ $< ../config.o ../rtcp.o ../utils.o $(LIBS)
14 21

  
15 22
clean:
16 23
	rm -f *.so *.o
plugins/janus_audiobridge.c
41 41
#include "../mutex.h"
42 42
#include "../rtp.h"
43 43
#include "../rtcp.h"
44
#include "../utils.h"
44 45

  
45 46

  
46 47
/* Plugin information */
......
882 883
			}
883 884
			JANUS_PRINT("Opus payload type is %d\n", participant->opus_pt);
884 885
			g_sprintf(sdp, sdp_template,
885
				g_get_monotonic_time(),			/* We need current time here */
886
				g_get_monotonic_time(),			/* We need current time here */
886
				janus_get_monotonic_time(),		/* We need current time here */
887
				janus_get_monotonic_time(),		/* We need current time here */
887 888
				participant->room->room_name,	/* Audio bridge name */
888 889
				participant->opus_pt,			/* Opus payload type */
889 890
				participant->opus_pt,			/* Opus payload type */
......
895 896
				g_strlcat(sdp, "m=video 0 RTP/SAVPF 0\r\n", 1024);				
896 897
			}
897 898
			/* How long will the gateway take to push the event? */
898
			gint64 start = g_get_monotonic_time();
899
			gint64 start = janus_get_monotonic_time();
899 900
			int res = gateway->push_event(msg->handle, &janus_audiobridge_plugin, msg->transaction, event_text, type, sdp);
900
			JANUS_PRINT("  >> Pushing event: %d (took %"SCNu64" ms)\n", res, g_get_monotonic_time()-start);
901
			JANUS_PRINT("  >> Pushing event: %d (took %"SCNu64" ms)\n", res, janus_get_monotonic_time()-start);
901 902
			if(res != JANUS_OK) {
902 903
				/* TODO Failed to negotiate? We should remove this participant */
903 904
			} else {
plugins/janus_echotest.c
28 28

  
29 29
#include "../config.h"
30 30
#include "../rtcp.h"
31
#include "../utils.h"
31 32

  
32 33

  
33 34
/* Plugin information */
......
418 419
			if(!strcasecmp(msg->sdp_type, "answer"))
419 420
				type = "offer";
420 421
			/* How long will the gateway take to push the event? */
421
			gint64 start = g_get_monotonic_time();
422
			gint64 start = janus_get_monotonic_time();
422 423
			int res = gateway->push_event(msg->handle, &janus_echotest_plugin, msg->transaction, event_text, type, msg->sdp);
423 424
			JANUS_PRINT("  >> Pushing event: %d (took %"SCNu64" ms)\n",
424
				res, g_get_monotonic_time()-start);
425
				res, janus_get_monotonic_time()-start);
425 426
		}
426 427
		continue;
427 428
		
plugins/janus_streaming.c
71 71

  
72 72
#include "../config.h"
73 73
#include "../rtp.h"
74
#include "../utils.h"
74 75

  
75 76

  
76 77
/* Plugin information */
......
732 733
			memset(sdptemp, 0, 1024);
733 734
			gchar buffer[100];
734 735
			memset(buffer, 0, 100);
735
			gint64 sessid = g_get_monotonic_time();
736
			gint64 sessid = janus_get_monotonic_time();
736 737
			gint64 version = sessid;	/* FIXME This needs to be increased when it changes, so time should be ok */
737 738
			g_sprintf(buffer,
738 739
				"v=0\r\no=%s %"SCNu64" %"SCNu64" IN IP4 127.0.0.1\r\n",
plugins/janus_videoroom.c
39 39
publishers = <max number of concurrent senders> (e.g., 6 for a video
40 40
             conference or 1 for a webinar)
41 41
bitrate = <max video bitrate for senders> (e.g., 128000)
42
fir_freq = <send a FIR to publishers every fir_freq seconds> (0=disable)
42 43
\endverbatim
43 44
 *
44 45
 * \ingroup plugins
......
51 52

  
52 53
#include "../config.h"
53 54
#include "../rtcp.h"
55
#include "../utils.h"
54 56

  
55 57

  
56 58
/* Plugin information */
......
133 135
	gchar *room_name;	/* Room description */
134 136
	int max_publishers;	/* Maximum number of concurrent publishers */
135 137
	uint64_t bitrate;	/* Global bitrate limit */
138
	uint16_t fir_freq;	/* Regular FIR frequency (0=disabled) */
136 139
	gboolean destroy;
137 140
	GHashTable *participants;	/* Map of potential publishers (we get listeners from them) */
138 141
} janus_videoroom;
......
156 159
	gchar *sdp;			/* The SDP this publisher negotiated, if any */
157 160
	gboolean audio_active;
158 161
	gboolean video_active;
162
	gboolean firefox;	/* We send Firefox users a different kind of FIR */
159 163
	uint64_t bitrate;
160 164
	gint64 fir_latest;	/* Time of latest sent FIR (to avoid flooding) */
161 165
	gint fir_seq;		/* FIR sequence number */
......
213 217
			janus_config_item *desc = janus_config_get_item(cat, "description");
214 218
			janus_config_item *bitrate = janus_config_get_item(cat, "bitrate");
215 219
			janus_config_item *maxp = janus_config_get_item(cat, "publishers");
220
			janus_config_item *firfreq = janus_config_get_item(cat, "fir_freq");
216 221
			/* Create the video mcu room */
217 222
			janus_videoroom *videoroom = calloc(1, sizeof(janus_videoroom));
218 223
			if(videoroom == NULL) {
......
238 243
			videoroom->bitrate = 0;
239 244
			if(bitrate != NULL && bitrate->value != NULL)
240 245
				videoroom->bitrate = atol(bitrate->value);
246
			videoroom->fir_freq = 0;
247
			if(firfreq != NULL && firfreq->value != NULL)
248
				videoroom->fir_freq = atol(firfreq->value);
241 249
			videoroom->destroy = 0;
242 250
			videoroom->participants = g_hash_table_new(NULL, NULL);
243 251
			g_hash_table_insert(rooms, GUINT_TO_POINTER(videoroom->room_id), videoroom);
......
254 262
	GList *r = rooms_list;
255 263
	while(r) {
256 264
		janus_videoroom *vr = (janus_videoroom *)r->data;
257
		JANUS_PRINT("  ::: [%"SCNu64"][%s] %"SCNu64", max %d publishers\n", vr->room_id, vr->room_name, vr->bitrate, vr->max_publishers);
265
		JANUS_PRINT("  ::: [%"SCNu64"][%s] %"SCNu64", max %d publishers, FIR frequency of %d seconds\n", vr->room_id, vr->room_name, vr->bitrate, vr->max_publishers, vr->fir_freq);
258 266
		r = r->next;
259 267
	}
260 268
	g_list_free(rooms_list);
......
401 409
				/* Send a FIR */
402 410
				char buf[20];
403 411
				memset(buf, 0, 20);
404
				janus_rtcp_fir((char *)&buf, 20, &p->fir_seq);
412
				if(!p->firefox)
413
					janus_rtcp_fir((char *)&buf, 20, &p->fir_seq);
414
				else
415
					janus_rtcp_fir_legacy((char *)&buf, 20, &p->fir_seq);
405 416
				JANUS_PRINT("New listener available, sending FIR to %s\n", p->display);
406 417
				gateway->relay_rtcp(p->session->handle, 1, buf, 20);
407 418
				/* Send a PLI too, just in case... */
......
427 438
		packet.length = len;
428 439
		packet.is_video = video;
429 440
		g_slist_foreach(participant->listeners, janus_videoroom_relay_rtp_packet, &packet);
430
		if(video) {
441
		if(video && (participant->room->fir_freq > 0)) {
431 442
			/* FIXME Very ugly hack to generate RTCP every tot seconds/frames */
432
			gint64 now = g_get_monotonic_time();
433
			if((now-participant->fir_latest) >= (10*G_USEC_PER_SEC)) {
434
				/* FIXME We send a FIR every 10 seconds */
443
			gint64 now = janus_get_monotonic_time();
444
			if((now-participant->fir_latest) >= (participant->room->fir_freq*G_USEC_PER_SEC)) {
445
				/* FIXME We send a FIR every tot seconds */
435 446
				participant->fir_latest = now;
436 447
				char buf[20];
437 448
				memset(buf, 0, 20);
438
				janus_rtcp_fir((char *)&buf, 20, &participant->fir_seq);
449
				if(!participant->firefox)
450
					janus_rtcp_fir((char *)&buf, 20, &participant->fir_seq);
451
				else
452
					janus_rtcp_fir_legacy((char *)&buf, 20, &participant->fir_seq);
439 453
				JANUS_PRINT("Sending FIR to %s\n", participant->display);
440 454
				gateway->relay_rtcp(handle, video, buf, 20);
441 455
				/* Send a PLI too, just in case... */
442 456
				memset(buf, 0, 12);
443 457
				janus_rtcp_pli((char *)&buf, 12);
444
				JANUS_PRINT("New listener available, sending PLI to %s\n", participant->display);
458
				JANUS_PRINT("Sending PLI to %s\n", participant->display);
445 459
				gateway->relay_rtcp(handle, video, buf, 12);
446 460
			}
447 461
		}
......
583 597
				sprintf(error_cause, "JSON error: invalid element (bitrate)");
584 598
				goto error;
585 599
			}
600
			json_t *fir_freq = json_object_get(root, "fir_freq");
601
			if(fir_freq && !json_is_integer(fir_freq)) {
602
				JANUS_DEBUG("JSON error: invalid element (fir_freq)\n");
603
				sprintf(error_cause, "JSON error: invalid element (fir_freq)");
604
				goto error;
605
			}
586 606
			json_t *publishers = json_object_get(root, "publishers");
587 607
			if(publishers && !json_is_integer(publishers)) {
588 608
				JANUS_DEBUG("JSON error: invalid element (publishers)\n");
......
627 647
			videoroom->bitrate = 0;
628 648
			if(bitrate)
629 649
				videoroom->bitrate = json_integer_value(bitrate);
650
			videoroom->fir_freq = 0;
651
			if(fir_freq)
652
				videoroom->fir_freq = json_integer_value(fir_freq);
630 653
			videoroom->destroy = 0;
631 654
			videoroom->participants = g_hash_table_new(NULL, NULL);
632 655
			g_hash_table_insert(rooms, GUINT_TO_POINTER(videoroom->room_id), videoroom);
......
636 659
			GList *r = rooms_list;
637 660
			while(r) {
638 661
				janus_videoroom *vr = (janus_videoroom *)r->data;
639
				JANUS_PRINT("  ::: [%"SCNu64"][%s] %"SCNu64", max %d publishers\n", vr->room_id, vr->room_name, vr->bitrate, vr->max_publishers);
662
				JANUS_PRINT("  ::: [%"SCNu64"][%s] %"SCNu64", max %d publishers, FIR frequency of %d seconds\n", vr->room_id, vr->room_name, vr->bitrate, vr->max_publishers, vr->fir_freq);
640 663
				r = r->next;
641 664
			}
642 665
			g_list_free(rooms_list);
......
721 744
				publisher->sdp = NULL;	/* We'll deal with this later */
722 745
				publisher->audio_active = FALSE;
723 746
				publisher->video_active = FALSE;
747
				publisher->firefox = FALSE;
724 748
				publisher->bitrate = videoroom->bitrate;
725 749
				publisher->listeners = NULL;
726 750
				publisher->fir_latest = 0;
......
791 815
					/* Negotiate by sending the selected publisher SDP back */
792 816
					if(publisher->sdp != NULL) {
793 817
						/* How long will the gateway take to push the event? */
794
						gint64 start = g_get_monotonic_time();
818
						gint64 start = janus_get_monotonic_time();
795 819
						int res = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event_text, "offer", publisher->sdp);
796
						JANUS_PRINT("  >> Pushing event: %d (took %"SCNu64" ms)\n", res, g_get_monotonic_time()-start);
820
						JANUS_PRINT("  >> Pushing event: %d (took %"SCNu64" ms)\n", res, janus_get_monotonic_time()-start);
797 821
						if(res != JANUS_OK) {
798 822
							/* TODO Failed to negotiate? We should remove this listener */
799 823
						} else {
......
883 907
			janus_videoroom_listener *listener = (janus_videoroom_listener *)session->participant;
884 908
			if(!strcasecmp(request_text, "start")) {
885 909
				/* Start/restart receiving the publisher streams */
910
				janus_videoroom_participant *publisher = listener->feed;
886 911
				listener->paused = FALSE;
912
				event = json_object();
913
				json_object_set(event, "videoroom", json_string("event"));
914
				json_object_set(event, "room", json_integer(publisher->room->room_id));
915
				json_object_set(event, "result", json_string("ok"));
916
				/* Send a FIR */
917
				char buf[20];
918
				memset(buf, 0, 20);
919
				if(!publisher->firefox)
920
					janus_rtcp_fir((char *)&buf, 20, &publisher->fir_seq);
921
				else
922
					janus_rtcp_fir_legacy((char *)&buf, 20, &publisher->fir_seq);
923
				JANUS_PRINT("Resuming publisher, sending FIR to %s\n", publisher->display);
924
				gateway->relay_rtcp(publisher->session->handle, 1, buf, 20);
925
				/* Send a PLI too, just in case... */
926
				memset(buf, 0, 12);
927
				janus_rtcp_pli((char *)&buf, 12);
928
				JANUS_PRINT("Resuming publisher, sending PLI to %s\n", publisher->display);
929
				gateway->relay_rtcp(publisher->session->handle, 1, buf, 12);
887 930
			} else if(!strcasecmp(request_text, "pause")) {
888 931
				/* Stop receiving the publisher streams for a while */
932
				janus_videoroom_participant *publisher = listener->feed;
889 933
				listener->paused = TRUE;
934
				event = json_object();
935
				json_object_set(event, "videoroom", json_string("event"));
936
				json_object_set(event, "room", json_integer(publisher->room->room_id));
937
				json_object_set(event, "result", json_string("ok"));
890 938
			} else if(!strcasecmp(request_text, "leave")) {
891 939
				janus_videoroom_participant *publisher = listener->feed;
892 940
				if(publisher != NULL) {
......
933 981
				msg->sdp = string_replace(msg->sdp, "sendrecv", "sendonly", &modified);	/* FIXME In case the browser doesn't set it correctly */
934 982
				msg->sdp = string_replace(msg->sdp, "sendonly", "recvonly", &modified);
935 983
				janus_videoroom_participant *participant = (janus_videoroom_participant *)session->participant;
984
				if(strstr(msg->sdp, "Mozilla")) {
985
					participant->firefox = TRUE;
986
				}
936 987
				/* How long will the gateway take to push the event? */
937
				gint64 start = g_get_monotonic_time();
988
				gint64 start = janus_get_monotonic_time();
938 989
				int res = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event_text, type, msg->sdp);
939
				JANUS_PRINT("  >> Pushing event: %d (took %"SCNu64" ms)\n", res, g_get_monotonic_time()-start);
990
				JANUS_PRINT("  >> Pushing event: %d (took %"SCNu64" ms)\n", res, janus_get_monotonic_time()-start);
940 991
				msg->sdp = string_replace(msg->sdp, "recvonly", "sendonly", &modified);
941 992
				if(res != JANUS_OK) {
942 993
					/* TODO Failed to negotiate? We should remove this publisher */
......
979 1030
					janus_videoroom_participant *feed = (janus_videoroom_participant *)listener->feed;
980 1031
					if(feed != NULL && feed->sdp != NULL) {
981 1032
						/* How long will the gateway take to push the event? */
982
						gint64 start = g_get_monotonic_time();
1033
						gint64 start = janus_get_monotonic_time();
983 1034
						int res = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event_text, type, feed->sdp);
984
						JANUS_PRINT("  >> Pushing event: %d (took %"SCNu64" ms)\n", res, g_get_monotonic_time()-start);
1035
						JANUS_PRINT("  >> Pushing event: %d (took %"SCNu64" ms)\n", res, janus_get_monotonic_time()-start);
985 1036
						if(res != JANUS_OK) {
986 1037
							/* TODO Failed to negotiate? We should remove this listener */
987 1038
						} else {
plugins/janus_voicemail.c
32 32

  
33 33
#include "../config.h"
34 34
#include "../rtp.h"
35
#include "../utils.h"
35 36

  
36 37

  
37 38
/* Plugin information */
......
333 334
	if(session->destroy)
334 335
		return;
335 336
	/* Only start recording this peer when we get this event */
336
	session->start_time = g_get_monotonic_time();
337
	session->start_time = janus_get_monotonic_time();
337 338
	session->started = TRUE;
338 339
	/* Prepare JSON event */
339 340
	json_t *event = json_object();
......
351 352
	janus_voicemail_session *session = (janus_voicemail_session *)handle->plugin_handle;	
352 353
	if(!session || session->destroy || session->stopping || !session->started || session->start_time == 0)
353 354
		return;
354
	gint64 now = g_get_monotonic_time();
355
	gint64 now = janus_get_monotonic_time();
355 356
	/* Have 10 seconds passed? */
356 357
	if((now-session->start_time) >= 10*G_USEC_PER_SEC) {
357 358
		/* FIXME Simulate a "stop" coming from the browser */
......
546 547
			}
547 548
			JANUS_PRINT("Opus payload type is %d\n", opus_pt);
548 549
			g_sprintf(sdp, sdp_template,
549
				g_get_monotonic_time(),			/* We need current time here */
550
				g_get_monotonic_time(),			/* We need current time here */
550
				janus_get_monotonic_time(),		/* We need current time here */
551
				janus_get_monotonic_time(),		/* We need current time here */
551 552
				session->recording_id,			/* Recording ID */
552 553
				opus_pt,						/* Opus payload type */
553 554
				opus_pt							/* Opus payload type */);
......
557 558
				g_strlcat(sdp, "m=video 0 RTP/SAVPF 0\r\n", 1024);				
558 559
			}
559 560
			/* How long will the gateway take to push the event? */
560
			gint64 start = g_get_monotonic_time();
561
			gint64 start = janus_get_monotonic_time();
561 562
			int res = gateway->push_event(msg->handle, &janus_voicemail_plugin, msg->transaction, event_text, type, sdp);
562
			JANUS_PRINT("  >> Pushing event: %d (took %"SCNu64" ms)\n", res, g_get_monotonic_time()-start);
563
			JANUS_PRINT("  >> Pushing event: %d (took %"SCNu64" ms)\n", res, janus_get_monotonic_time()-start);
563 564
			if(res != JANUS_OK) {
564 565
				/* TODO Failed to negotiate? We should remove this participant */
565 566
			}
rtcp.c
90 90
				}
91 91
				break;
92 92
			}
93
			case RTCP_FIR: {
94
				/* FIR, rfc2032 */
95
				JANUS_PRINT("     #%d FIR (192)\n", pno);
96
				rtcp_fb *rtcpfb = (rtcp_fb *)rtcp;
97
				if(fixssrc && newssrcr && (ntohs(rtcp->length) >= 20)) {
98
					rtcpfb->media = htonl(newssrcr);
99
				}
100
				if(fixssrc && newssrcr) {
101
					uint32_t *ssrc = (uint32_t *)rtcpfb->fci;
102
					*ssrc = htonl(newssrcr);
103
				}
104
				break;
105
			}
93 106
			case RTCP_RTPFB: {
94 107
				/* RTPFB, Transport layer FB message (rfc4585) */
95 108
				//~ JANUS_PRINT("     #%d RTPFB (205)\n", pno);
......
398 411
	return 0;
399 412
}
400 413

  
414
/* Generate a new legacy FIR message */
415
int janus_rtcp_fir_legacy(char *packet, int len, int *seqnr) {
416
	/* FIXME Right now, this is identical to the new FIR, with the difference that we use 192 as PT */
417
	if(packet == NULL || len != 20 || seqnr == NULL)
418
		return -1;
419
	rtcp_header *rtcp = (rtcp_header *)packet;
420
	*seqnr = *seqnr + 1;
421
	if(*seqnr < 0 || *seqnr >= 256)
422
		*seqnr = 0;	/* Reset sequence number */
423
	/* Set header */
424
	rtcp->version = 2;
425
	rtcp->type = RTCP_FIR;
426
	rtcp->rc = 4;	/* FMT=4 */
427
	rtcp->length = htons((len/4)-1);
428
	/* Now set FIR stuff */
429
	rtcp_fb *rtcpfb = (rtcp_fb *)rtcp;
430
	rtcp_fir *fir = (rtcp_fir *)rtcpfb->fci;
431
	fir->seqnr = htonl(*seqnr << 24);	/* FCI: Sequence number */
432
	JANUS_PRINT("[FIR] seqnr=%d (%d bytes)\n", *seqnr, 4*(ntohs(rtcp->length)+1));
433
	return 0;
434
}
435

  
401 436
/* Generate a new PLI message */
402 437
int janus_rtcp_pli(char *packet, int len) {
403 438
	if(packet == NULL || len != 12)
rtcp.h
221 221
 * @returns 0 in case of success, -1 on errors */
222 222
int janus_rtcp_fir(char *packet, int len, int *seqnr);
223 223

  
224
/*! \brief Method to generate a new legacy RTCP FIR (RFC2032) message to request a key frame
225
 * \note This is actually identical to janus_rtcp_fir(), with the difference that we set 192 as packet type
226
 * @param[in] packet The buffer data (MUST be at least 20 chars)
227
 * @param[in] len The message data length in bytes (MUST be 20)
228
 * @param[in,out] seqnr The current FIR sequence number (will be incremented by the method)
229
 * @returns 0 in case of success, -1 on errors */
230
int janus_rtcp_fir_legacy(char *packet, int len, int *seqnr);
231

  
224 232
/*! \brief Method to generate a new RTCP PLI message to request a key frame
225 233
 * @param[in] packet The buffer data (MUST be at least 20 chars)
226 234
 * @param[in] len The message data length in bytes (MUST be 8)
sdp.c
18 18
#include "ice.h"
19 19
#include "dtls.h"
20 20
#include "sdp.h"
21
#include "utils.h"
21 22

  
22 23

  
23 24
static su_home_t *home = NULL;
......
476 477
				anon->sdp_origin->o_id, anon->sdp_origin->o_version);
477 478
		g_strlcat(sdp, buffer, BUFSIZE);
478 479
	} else {
479
		gint64 sessid = g_get_monotonic_time();
480
		gint64 sessid = janus_get_monotonic_time();
480 481
		gint64 version = sessid;	/* FIXME This needs to be increased when it changes, so time should be ok */
481 482
		g_sprintf(buffer,
482 483
			"o=%s %"SCNi64" %"SCNi64" IN IP4 127.0.0.1\r\n",	/* FIXME Should we fix the address? */
......
506 507
	//~ /* Connection c= (global) */
507 508
	//~ if(anon->sdp_connection) {
508 509
		//~ g_sprintf(buffer,
509
			//~ "c=IN IP4 %s\r\n", janus_get_local_ip());
510
			//~ "c=IN IP4 %s\r\n", janus_get_public_ip());
510 511
		//~ g_strlcat(sdp, buffer, BUFSIZE);
511 512
	//~ }
512 513
	/* DTLS fingerprint a= (global) */
......
612 613
			/* Media connection c= */
613 614
			//~ if(m->m_connections) {
614 615
				g_sprintf(buffer,
615
					"c=IN IP4 %s\r\n", janus_get_local_ip());
616
					"c=IN IP4 %s\r\n", janus_get_public_ip());
616 617
				g_strlcat(sdp, buffer, BUFSIZE);
617 618
			//~ }
618 619
			/* What is the direction? */
......
633 634
			}
634 635
			/* RTCP */
635 636
			g_sprintf(buffer, "a=rtcp:%s IN IP4 %s\r\n",
636
				m->m_type == sdp_media_audio ? "ARTCP" : "VRTCP", janus_get_local_ip());
637
				m->m_type == sdp_media_audio ? "ARTCP" : "VRTCP", janus_get_public_ip());
637 638
			g_strlcat(sdp, buffer, BUFSIZE);
638 639
			/* RTP maps */
639 640
			if(m->m_rtpmaps) {
......
689 690
					"a=ssrc:%i msid:janus janusa0\r\n"
690 691
					"a=ssrc:%i mslabel:janus\r\n"
691 692
					"a=ssrc:%i label:janusa0\r\n",
692
						//~ janus_get_local_ip(),
693
						//~ janus_get_public_ip(),
693 694
						stream->ssrc, stream->ssrc, stream->ssrc, stream->ssrc);
694 695
				g_strlcat(sdp, buffer, BUFSIZE);
695 696
			} else if(m->m_type == sdp_media_video) {
......
699 700
					"a=ssrc:%i msid:janus janusv0\r\n"
700 701
					"a=ssrc:%i mslabel:janus\r\n"
701 702
					"a=ssrc:%i label:janusv0\r\n",
702
						//~ janus_get_local_ip(),
703
						//~ janus_get_public_ip(),
703 704
						stream->ssrc, stream->ssrc, stream->ssrc, stream->ssrc);
704 705
				g_strlcat(sdp, buffer, BUFSIZE);
705 706
			}
utils.c
1
/*! \file    utils.c
2
 * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3
 * \copyright GNU Affero 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 "utils.h"
13

  
14
gint64 janus_get_monotonic_time() {
15
	struct timespec ts;
16
	clock_gettime (CLOCK_MONOTONIC, &ts);
17
	return (ts.tv_sec*G_GINT64_CONSTANT(1000000)) + (ts.tv_nsec/G_GINT64_CONSTANT(1000));
18
}
utils.h
1
/*! \file    utils.h
2
 * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3
 * \copyright GNU Affero General Public License v3
4
 * \brief    Utilities and helpers (headers)
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
#ifndef _JANUS_UTILS_H
13
#define _JANUS_UTILS_H
14

  
15
#include <glib.h>
16

  
17
/*! \brief Helper to retrieve the system monotonic time, as Glib's
18
 * g_get_monotonic_time may not be available (only since 2.28)
19
 * @returns The system monotonic time */
20
gint64 janus_get_monotonic_time(void);
21

  
22
#endif

Also available in: Unified diff