[Pkg-voip-commits] [janus] 02/282: Several new helper methods for SDP utilities
Jonas Smedegaard
dr at jones.dk
Wed Dec 20 21:53:21 UTC 2017
This is an automated email from the git hooks/post-receive script.
js pushed a commit to annotated tag debian/0.2.6-1
in repository janus.
commit b310383cea8d706ced5daa131fe2e74e20bc57a5
Author: Lorenzo Miniero <lminiero at gmail.com>
Date: Tue Feb 14 17:39:51 2017 +0100
Several new helper methods for SDP utilities
---
plugins/janus_audiobridge.c | 87 ++--
plugins/janus_echotest.c | 48 +-
plugins/janus_videoroom.c | 1160 +++++++------------------------------------
sdp-utils.c | 728 ++++++++++++++++++++++++---
sdp-utils.h | 141 +++++-
utils.c | 9 +
6 files changed, 1051 insertions(+), 1122 deletions(-)
diff --git a/plugins/janus_audiobridge.c b/plugins/janus_audiobridge.c
index 6c8ece3..c6fd498 100644
--- a/plugins/janus_audiobridge.c
+++ b/plugins/janus_audiobridge.c
@@ -543,6 +543,7 @@ record_file = /path/to/recording.wav (where to save the recording)
#include "../rtp.h"
#include "../rtcp.h"
#include "../record.h"
+#include "../sdp-utils.h"
#include "../utils.h"
@@ -859,18 +860,6 @@ static gint janus_audiobridge_rtp_sort(gconstpointer a, gconstpointer b) {
return 0;
}
-/* SDP offer/answer template */
-#define sdp_template \
- "v=0\r\n" \
- "o=- %"SCNu64" %"SCNu64" IN IP4 127.0.0.1\r\n" /* We need current time here */ \
- "s=%s\r\n" /* Audio bridge name */ \
- "t=0 0\r\n" \
- "m=audio 1 RTP/SAVPF %d\r\n" /* Opus payload type */ \
- "c=IN IP4 1.1.1.1\r\n" \
- "a=rtpmap:%d opus/48000/2\r\n" /* Opus payload type */ \
- "a=fmtp:%d maxplaybackrate=%"SCNu32"; stereo=0; sprop-stereo=0; useinbandfec=0\r\n" /* Opus payload type and room sampling rate */ \
- "%s" /* extmap(s), if any */
-
/* Helper struct to generate and parse WAVE headers */
typedef struct wav_header {
char riff[4];
@@ -915,6 +904,7 @@ typedef struct wav_header {
#define JANUS_AUDIOBRIDGE_ERROR_ID_EXISTS 490
#define JANUS_AUDIOBRIDGE_ERROR_ALREADY_JOINED 491
#define JANUS_AUDIOBRIDGE_ERROR_NO_SUCH_USER 492
+#define JANUS_AUDIOBRIDGE_ERROR_INVALID_SDP 493
/* AudioBridge watchdog/garbage collector (sort of) */
@@ -3238,54 +3228,55 @@ static void *janus_audiobridge_handler(void *data) {
json_decref(event);
} else {
JANUS_LOG(LOG_VERB, "This is involving a negotiation (%s) as well:\n%s\n", msg_sdp_type, msg_sdp);
- const char *type = NULL;
- if(!strcasecmp(msg_sdp_type, "offer"))
- type = "answer";
- if(!strcasecmp(msg_sdp_type, "answer"))
- type = "offer";
- /* Fill the SDP template and use that as our answer */
- janus_audiobridge_participant *participant = (janus_audiobridge_participant *)session->participant;
- char sdp[1024];
+ /* Prepare an SDP answer */
+ const char *type = "answer";
+ char error_str[512];
+ janus_sdp *offer = janus_sdp_parse(msg_sdp, error_str, sizeof(error_str));
+ if(offer == NULL) {
+ json_decref(event);
+ JANUS_LOG(LOG_ERR, "Error parsing offer: %s\n", error_str);
+ error_code = JANUS_AUDIOBRIDGE_ERROR_INVALID_SDP;
+ g_snprintf(error_cause, 512, "Error parsing offer: %s", error_str);
+ goto error;
+ }
/* What is the Opus payload type? */
- participant->opus_pt = janus_get_codec_pt(msg_sdp, "opus");
+ janus_audiobridge_participant *participant = (janus_audiobridge_participant *)session->participant;
+ participant->opus_pt = janus_sdp_get_codec_pt(offer, "opus");
+ if(participant->opus_pt < 0) {
+ /* TODO Handle this case */
+ JANUS_LOG(LOG_ERR, "Offer doesn't contain Opus..?\n");
+ }
JANUS_LOG(LOG_VERB, "Opus payload type is %d\n", participant->opus_pt);
+ janus_sdp *answer = janus_sdp_generate_answer(offer,
+ /* Reject video and data channels, if offered */
+ JANUS_SDP_OA_VIDEO, FALSE,
+ JANUS_SDP_OA_DATA, FALSE,
+ JANUS_SDP_OA_DONE);
+ /* Replace the session name */
+ g_free(answer->s_name);
+ answer->s_name = g_strdup(participant->room->room_name);
+ /* Add a fmtp attribute */
+ janus_sdp_attribute *a = janus_sdp_attribute_create("fmtp",
+ "%d maxplaybackrate=%"SCNu32"; stereo=0; sprop-stereo=0; useinbandfec=0\r\n",
+ participant->opus_pt, participant->room->sampling_rate);
+ janus_sdp_attribute_add_to_mline(janus_sdp_mline_find(answer, JANUS_SDP_AUDIO), a);
/* Is the audio level extension negotiated? */
participant->extmap_id = 0;
participant->dBov_level = 0;
int extmap_id = -1;
- char audio_level_extmap[100];
if(participant->room && participant->room->audiolevel_ext)
extmap_id = janus_rtp_header_extension_get_id(msg_sdp, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
if(extmap_id > -1) {
+ /* Add an extmap attribute too */
participant->extmap_id = extmap_id;
- g_snprintf(audio_level_extmap, sizeof(audio_level_extmap),
- "a=extmap:%d %s\r\n", extmap_id, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
+ janus_sdp_attribute *a = janus_sdp_attribute_create("extmap",
+ "%d %s\r\n", extmap_id, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
+ janus_sdp_attribute_add_to_mline(janus_sdp_mline_find(answer, JANUS_SDP_AUDIO), a);
}
/* Prepare the response */
- g_snprintf(sdp, 1024, sdp_template,
- janus_get_real_time(), /* We need current time here */
- janus_get_real_time(), /* We need current time here */
- participant->room->room_name, /* Audio bridge name */
- participant->opus_pt, /* Opus payload type */
- participant->opus_pt, /* Opus payload type */
- participant->opus_pt, /* Opus payload type and room sampling rate */
- participant->room->sampling_rate,
- extmap_id > -1 ? audio_level_extmap : "");
- /* Is the peer recvonly? */
- if(strstr(msg_sdp, "a=recvonly") != NULL) {
- /* If so, use sendonly here */
- g_strlcat(sdp, "a=sendonly\r\n", 1024);
- }
- /* Did the peer negotiate video? */
- if(strstr(msg_sdp, "m=video") != NULL) {
- /* If so, reject it */
- g_strlcat(sdp, "m=video 0 RTP/SAVPF 0\r\n", 1024);
- }
- /* Did the peer negotiate data channels? */
- if(strstr(msg_sdp, "DTLS/SCTP") != NULL) {
- /* If so, reject them */
- g_strlcat(sdp, "m=application 0 DTLS/SCTP 0\r\n", 1024);
- }
+ char *sdp = janus_sdp_write(answer);
+ janus_sdp_free(offer);
+ janus_sdp_free(answer);
json_t *jsep = json_pack("{ssss}", "type", type, "sdp", sdp);
/* How long will the gateway take to push the event? */
g_atomic_int_set(&session->hangingup, 0);
diff --git a/plugins/janus_echotest.c b/plugins/janus_echotest.c
index 0497864..5fed114 100644
--- a/plugins/janus_echotest.c
+++ b/plugins/janus_echotest.c
@@ -93,6 +93,7 @@
#include "../mutex.h"
#include "../record.h"
#include "../rtcp.h"
+#include "../sdp-utils.h"
#include "../utils.h"
@@ -219,6 +220,7 @@ static void janus_echotest_message_free(janus_echotest_message *msg) {
#define JANUS_ECHOTEST_ERROR_NO_MESSAGE 411
#define JANUS_ECHOTEST_ERROR_INVALID_JSON 412
#define JANUS_ECHOTEST_ERROR_INVALID_ELEMENT 413
+#define JANUS_ECHOTEST_ERROR_INVALID_SDP 414
/* EchoTest watchdog/garbage collector (sort of) */
@@ -890,39 +892,21 @@ static void *janus_echotest_handler(void *data) {
JANUS_LOG(LOG_VERB, " >> %d (%s)\n", ret, janus_get_api_error(ret));
json_decref(event);
} else {
- /* Forward the same offer to the gateway, to start the echo test */
- const char *type = NULL;
- if(!strcasecmp(msg_sdp_type, "offer"))
- type = "answer";
- if(!strcasecmp(msg_sdp_type, "answer"))
- type = "offer";
- /* Any media direction that needs to be fixed? */
- char *sdp = g_strdup(msg_sdp);
- if(strstr(sdp, "a=recvonly")) {
- /* Turn recvonly to inactive, as we simply bounce media back */
- sdp = janus_string_replace(sdp, "a=recvonly", "a=inactive");
- } else if(strstr(sdp, "a=sendonly")) {
- /* Turn sendonly to recvonly */
- sdp = janus_string_replace(sdp, "a=sendonly", "a=recvonly");
- /* FIXME We should also actually not echo this media back, though... */
- }
- /* Make also sure we get rid of ULPfec, red, etc. */
- if(strstr(sdp, "ulpfec")) {
- /* FIXME This really needs some better code */
- sdp = janus_string_replace(sdp, "a=rtpmap:116 red/90000\r\n", "");
- sdp = janus_string_replace(sdp, "a=rtpmap:117 ulpfec/90000\r\n", "");
- sdp = janus_string_replace(sdp, "a=rtpmap:96 rtx/90000\r\n", "");
- sdp = janus_string_replace(sdp, "a=fmtp:96 apt=100\r\n", "");
- sdp = janus_string_replace(sdp, "a=rtpmap:97 rtx/90000\r\n", "");
- sdp = janus_string_replace(sdp, "a=fmtp:97 apt=101\r\n", "");
- sdp = janus_string_replace(sdp, "a=rtpmap:98 rtx/90000\r\n", "");
- sdp = janus_string_replace(sdp, "a=fmtp:98 apt=116\r\n", "");
- sdp = janus_string_replace(sdp, " 116", "");
- sdp = janus_string_replace(sdp, " 117", "");
- sdp = janus_string_replace(sdp, " 96", "");
- sdp = janus_string_replace(sdp, " 97", "");
- sdp = janus_string_replace(sdp, " 98", "");
+ /* Answer the offer and send it to the gateway, to start the echo test */
+ const char *type = "answer";
+ char error_str[512];
+ janus_sdp *offer = janus_sdp_parse(msg_sdp, error_str, sizeof(error_str));
+ if(offer == NULL) {
+ json_decref(event);
+ JANUS_LOG(LOG_ERR, "Error parsing offer: %s\n", error_str);
+ error_code = JANUS_ECHOTEST_ERROR_INVALID_SDP;
+ g_snprintf(error_cause, 512, "Error parsing offer: %s", error_str);
+ goto error;
}
+ janus_sdp *answer = janus_sdp_generate_answer(offer, JANUS_SDP_OA_DONE);
+ char *sdp = janus_sdp_write(answer);
+ janus_sdp_free(offer);
+ janus_sdp_free(answer);
json_t *jsep = json_pack("{ssss}", "type", type, "sdp", sdp);
/* How long will the gateway take to push the event? */
g_atomic_int_set(&session->hangingup, 0);
diff --git a/plugins/janus_videoroom.c b/plugins/janus_videoroom.c
index a08b760..ed55f6d 100644
--- a/plugins/janus_videoroom.c
+++ b/plugins/janus_videoroom.c
@@ -62,7 +62,7 @@ publishers = <max number of concurrent senders> (e.g., 6 for a video
conference or 1 for a webinar)
bitrate = <max video bitrate for senders> (e.g., 128000)
fir_freq = <send a FIR to publishers every fir_freq seconds> (0=disable)
-audiocodec = opus|isac32|isac16|pcmu|pcma (audio codec to force on publishers, default=opus)
+audiocodec = opus|isac32|isac16|pcmu|pcma|g722 (audio codec to force on publishers, default=opus)
videocodec = vp8|vp9|h264 (video codec to force on publishers, default=vp8)
audiolevel_ext = yes|no (whether the ssrc-audio-level RTP extension must be
negotiated/used or not for new publishers, default=yes)
@@ -110,10 +110,8 @@ rec_dir = <folder where recordings should be stored, when enabled>
* the \c switch request can be used to change the source of the media
* flowing over a specific PeerConnection (e.g., I was watching Alice,
* I want to watch Bob now) without having to create a new handle for
- * that; \c stop interrupts a viewer instance; \c add and \c remove
- * are just used when involving "Plan B", and are used to add or remove
- * publishers to be muxed in the single viewer PeerConnection; finally,
- * \c leave allows you to leave a video room for good.
+ * that; \c stop interrupts a viewer instance; finally, \c leave allows
+ * you to leave a video room for good.
*
* Notice that, in general, all users can create rooms. If you want to
* limit this functionality, you can configure an admin \c admin_key in
@@ -302,9 +300,6 @@ static struct janus_json_parameter listener_parameters[] = {
{"video", JANUS_JSON_BOOL, 0},
{"data", JANUS_JSON_BOOL, 0}
};
-static struct janus_json_parameter feeds_parameters[] = {
- {"feeds", JSON_ARRAY, JANUS_JSON_PARAM_NONEMPTY}
-};
/* Static configuration instance */
static janus_config *config = NULL;
@@ -324,7 +319,6 @@ static void janus_videoroom_relay_data_packet(gpointer data, gpointer user_data)
typedef enum janus_videoroom_p_type {
janus_videoroom_p_type_none = 0,
janus_videoroom_p_type_subscriber, /* Generic listener/subscriber */
- janus_videoroom_p_type_subscriber_muxed, /* Multiplexed listener/subscriber */
janus_videoroom_p_type_publisher, /* Participant/publisher */
} janus_videoroom_p_type;
@@ -355,12 +349,24 @@ static void janus_videoroom_message_free(janus_videoroom_message *msg) {
g_free(msg);
}
+/* Payload types we'll offer internally */
+#define OPUS_PT 111
+#define ISAC32_PT 104
+#define ISAC16_PT 103
+#define PCMU_PT 0
+#define PCMA_PT 8
+#define G722_PT 9
+#define VP8_PT 96
+#define VP9_PT 101
+#define H264_PT 107
+
typedef enum janus_videoroom_audiocodec {
JANUS_VIDEOROOM_OPUS, /* Publishers will have to use OPUS */
JANUS_VIDEOROOM_ISAC_32K, /* Publishers will have to use ISAC 32K */
JANUS_VIDEOROOM_ISAC_16K, /* Publishers will have to use ISAC 16K */
JANUS_VIDEOROOM_PCMU, /* Publishers will have to use PCMU 8K */
- JANUS_VIDEOROOM_PCMA /* Publishers will have to use PCMA 8K */
+ JANUS_VIDEOROOM_PCMA, /* Publishers will have to use PCMA 8K */
+ JANUS_VIDEOROOM_G722 /* Publishers will have to use G.722 */
} janus_videoroom_audiocodec;
static const char *janus_videoroom_audiocodec_name(janus_videoroom_audiocodec acodec) {
switch(acodec) {
@@ -374,11 +380,32 @@ static const char *janus_videoroom_audiocodec_name(janus_videoroom_audiocodec ac
return "pcmu";
case JANUS_VIDEOROOM_PCMA:
return "pcma";
+ case JANUS_VIDEOROOM_G722:
+ return "g722";
default:
/* Shouldn't happen */
return "opus";
}
}
+static int janus_videoroom_audiocodec_pt(janus_videoroom_audiocodec acodec) {
+ switch(acodec) {
+ case JANUS_VIDEOROOM_OPUS:
+ return OPUS_PT;
+ case JANUS_VIDEOROOM_ISAC_32K:
+ return ISAC32_PT;
+ case JANUS_VIDEOROOM_ISAC_16K:
+ return ISAC16_PT;
+ case JANUS_VIDEOROOM_PCMU:
+ return PCMU_PT;
+ case JANUS_VIDEOROOM_PCMA:
+ return PCMA_PT;
+ case JANUS_VIDEOROOM_G722:
+ return G722_PT;
+ default:
+ /* Shouldn't happen */
+ return OPUS_PT;
+ }
+}
typedef enum janus_videoroom_videocodec {
JANUS_VIDEOROOM_VP8, /* Publishers will have to use VP8 */
@@ -398,6 +425,19 @@ static const char *janus_videoroom_videocodec_name(janus_videoroom_videocodec vc
return "vp8";
}
}
+static int janus_videoroom_videocodec_pt(janus_videoroom_videocodec vcodec) {
+ switch(vcodec) {
+ case JANUS_VIDEOROOM_VP8:
+ return VP8_PT;
+ case JANUS_VIDEOROOM_VP9:
+ return VP9_PT;
+ case JANUS_VIDEOROOM_H264:
+ return H264_PT;
+ default:
+ /* Shouldn't happen */
+ return VP8_PT;
+ }
+}
typedef struct janus_videoroom {
guint64 room_id; /* Unique room ID */
@@ -506,19 +546,10 @@ typedef struct janus_videoroom_listener {
guint32 pvt_id; /* Private ID of the participant that is subscribing (if available/provided) */
janus_videoroom_listener_context context; /* Needed in case there are publisher switches on this listener */
gboolean audio, video, data; /* Whether audio, video and/or data must be sent to this publisher */
- struct janus_videoroom_listener_muxed *parent; /* Overall subscriber, if this is a sub-listener in a Multiplexed one */
gboolean paused;
} janus_videoroom_listener;
static void janus_videoroom_listener_free(janus_videoroom_listener *l);
-typedef struct janus_videoroom_listener_muxed {
- janus_videoroom_session *session;
- janus_videoroom *room; /* Room */
- GSList *listeners; /* List of listeners (as a Multiplexed listener can be subscribed to more publishers at the same time) */
- janus_mutex listeners_mutex;
-} janus_videoroom_listener_muxed;
-static void janus_videoroom_muxed_listener_free(janus_videoroom_listener_muxed *l);
-
typedef struct janus_videoroom_rtp_relay_packet {
rtp_header *data;
gint length;
@@ -527,96 +558,6 @@ typedef struct janus_videoroom_rtp_relay_packet {
uint16_t seq_number;
} janus_videoroom_rtp_relay_packet;
-/* SDP offer/answer templates */
-#define OPUS_PT 111
-#define ISAC32_PT 104
-#define ISAC16_PT 103
-#define PCMU_PT 0
-#define PCMA_PT 8
-#define VP8_PT 100
-#define VP9_PT 101
-#define H264_PT 107
-#define sdp_template \
- "v=0\r\n" \
- "o=- %"SCNu64" %"SCNu64" IN IP4 127.0.0.1\r\n" /* We need current time here */ \
- "s=%s\r\n" /* Video room name */ \
- "t=0 0\r\n" \
- "%s%s%s" /* Audio, video and/or data channel m-lines */
-#define sdp_a_template_opus \
- "m=audio 1 RTP/SAVPF %d\r\n" /* Opus payload type */ \
- "c=IN IP4 1.1.1.1\r\n" \
- "a=%s\r\n" /* Media direction */ \
- "a=rtpmap:%d opus/48000/2\r\n" /* Opus payload type */ \
- "%s" /* extmap(s), if any */
-#define sdp_a_template_isac32 \
- "m=audio 1 RTP/SAVPF %d\r\n" /* ISAC32_PT payload type */ \
- "c=IN IP4 1.1.1.1\r\n" \
- "a=%s\r\n" /* Media direction */ \
- "a=rtpmap:%d ISAC/32000\r\n" /* ISAC32_PT payload type */ \
- "%s" /* extmap(s), if any */
-#define sdp_a_template_isac16 \
- "m=audio 1 RTP/SAVPF %d\r\n" /* ISAC16_PT payload type */ \
- "c=IN IP4 1.1.1.1\r\n" \
- "a=%s\r\n" /* Media direction */ \
- "a=rtpmap:%d ISAC/16000\r\n" /* ISAC16_PT payload type */ \
- "%s" /* extmap(s), if any */
-#define sdp_a_template_pcmu \
- "m=audio 1 RTP/SAVPF %d\r\n" /* PCMU_PT payload type */ \
- "c=IN IP4 1.1.1.1\r\n" \
- "a=%s\r\n" /* Media direction */ \
- "a=rtpmap:%d PCMU/8000\r\n" /* PCMU_PT payload type */ \
- "%s" /* extmap(s), if any */
-#define sdp_a_template_pcma \
- "m=audio 1 RTP/SAVPF %d\r\n" /* PCMA_PT payload type */ \
- "c=IN IP4 1.1.1.1\r\n" \
- "a=%s\r\n" /* Media direction */ \
- "a=rtpmap:%d PCMA/8000\r\n" /* PCMA_PT payload type */ \
- "%s" /* extmap(s), if any */
-#define sdp_v_template_vp8 \
- "m=video 1 RTP/SAVPF %d\r\n" /* VP8 payload type */ \
- "c=IN IP4 1.1.1.1\r\n" \
- "b=AS:%d\r\n" /* Bandwidth */ \
- "a=%s\r\n" /* Media direction */ \
- "a=rtpmap:%d VP8/90000\r\n" /* VP8 payload type */ \
- "a=rtcp-fb:%d ccm fir\r\n" /* VP8 payload type */ \
- "a=rtcp-fb:%d nack\r\n" /* VP8 payload type */ \
- "a=rtcp-fb:%d nack pli\r\n" /* VP8 payload type */ \
- "a=rtcp-fb:%d goog-remb\r\n" /* VP8 payload type */ \
- "%s" /* extmap(s), if any */
-#define sdp_v_template_vp9 \
- "m=video 1 RTP/SAVPF %d\r\n" /* VP9 payload type */ \
- "c=IN IP4 1.1.1.1\r\n" \
- "b=AS:%d\r\n" /* Bandwidth */ \
- "a=%s\r\n" /* Media direction */ \
- "a=rtpmap:%d VP9/90000\r\n" /* VP9 payload type */ \
- "a=rtcp-fb:%d ccm fir\r\n" /* VP9 payload type */ \
- "a=rtcp-fb:%d nack\r\n" /* VP9 payload type */ \
- "a=rtcp-fb:%d nack pli\r\n" /* VP9 payload type */ \
- "a=rtcp-fb:%d goog-remb\r\n" /* VP9 payload type */ \
- "%s" /* extmap(s), if any */
-#define sdp_v_template_h264 \
- "m=video 1 RTP/SAVPF %d\r\n" /* H264 payload type */ \
- "c=IN IP4 1.1.1.1\r\n" \
- "b=AS:%d\r\n" /* Bandwidth */ \
- "a=%s\r\n" /* Media direction */ \
- "a=rtpmap:%d H264/90000\r\n" /* H264 payload type */ \
- "a=fmtp:%d profile-level-id=42e01f;packetization-mode=1\r\n" \
- "a=rtcp-fb:%d ccm fir\r\n" /* H264 payload type */ \
- "a=rtcp-fb:%d nack\r\n" /* H264 payload type */ \
- "a=rtcp-fb:%d nack pli\r\n" /* H264 payload type */ \
- "a=rtcp-fb:%d goog-remb\r\n" /* H264 payload type */ \
- "%s" /* extmap(s), if any */
-#ifdef HAVE_SCTP
-#define sdp_d_template \
- "m=application 1 DTLS/SCTP 5000\r\n" \
- "c=IN IP4 1.1.1.1\r\n" \
- "a=sctpmap:5000 webrtc-datachannel 16\r\n"
-#else
-#define sdp_d_template \
- "m=application 0 DTLS/SCTP 0\r\n" \
- "c=IN IP4 1.1.1.1\r\n"
-#endif
-
/* Error codes */
#define JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR 499
@@ -639,11 +580,6 @@ typedef struct janus_videoroom_rtp_relay_packet {
#define JANUS_VIDEOROOM_ERROR_INVALID_SDP 437
-/* Multiplexing helpers */
-int janus_videoroom_muxed_subscribe(janus_videoroom_listener_muxed *muxed_listener, GList *feeds, char *transaction);
-int janus_videoroom_muxed_unsubscribe(janus_videoroom_listener_muxed *muxed_listener, GList *feeds, char *transaction);
-int janus_videoroom_muxed_offer(janus_videoroom_listener_muxed *muxed_listener, char *transaction, json_t *event);
-
static guint32 janus_videoroom_rtp_forwarder_add_helper(janus_videoroom_participant *p,
const gchar* host, int port, int pt, uint32_t ssrc, gboolean is_video, gboolean is_data) {
if(!p || !host) {
@@ -681,9 +617,6 @@ static void session_free(gpointer data) {
case janus_videoroom_p_type_subscriber:
janus_videoroom_listener_free(session->participant);
break;
- case janus_videoroom_p_type_subscriber_muxed:
- janus_videoroom_muxed_listener_free(session->participant);
- break;
default:
break;
}
@@ -886,6 +819,8 @@ int janus_videoroom_init(janus_callbacks *callback, const char *config_path) {
videoroom->acodec = JANUS_VIDEOROOM_PCMU;
else if(!strcasecmp(audiocodec->value, "pcma"))
videoroom->acodec = JANUS_VIDEOROOM_PCMA;
+ else if(!strcasecmp(audiocodec->value, "g722"))
+ videoroom->acodec = JANUS_VIDEOROOM_G722;
else {
JANUS_LOG(LOG_WARN, "Unsupported audio codec '%s', falling back to OPUS\n", audiocodec->value);
videoroom->acodec = JANUS_VIDEOROOM_OPUS;
@@ -1138,8 +1073,6 @@ void janus_videoroom_destroy_session(janus_plugin_session *handle, int *error) {
janus_videoroom_leave_or_unpublish(participant, TRUE);
} else if(session->participant_type == janus_videoroom_p_type_subscriber) {
/* Detaching this listener from its publisher is already done by hangup_media */
- } else if(session->participant_type == janus_videoroom_p_type_subscriber_muxed) {
- /* Detaching this listener from its publishers is already done by hangup_media */
}
}
janus_mutex_unlock(&sessions_mutex);
@@ -1209,9 +1142,6 @@ json_t *janus_videoroom_query_session(janus_plugin_session *handle) {
json_object_set_new(media, "data", json_integer(participant->data));
json_object_set_new(info, "media", media);
}
- } else if(session->participant_type == janus_videoroom_p_type_subscriber_muxed) {
- json_object_set_new(info, "type", json_string("muxed-listener"));
- /* TODO */
}
}
json_object_set_new(info, "destroyed", json_integer(session->destroyed));
@@ -1335,10 +1265,12 @@ struct janus_plugin_result *janus_videoroom_handle_message(janus_plugin_session
json_t *audiocodec = json_object_get(root, "audiocodec");
if(audiocodec) {
const char *audiocodec_value = json_string_value(audiocodec);
- if(!strcasecmp(audiocodec_value, "opus") && !strcasecmp(audiocodec_value, "isac32") && !strcasecmp(audiocodec_value, "isac16") && !strcasecmp(audiocodec_value, "pcmu") && !strcasecmp(audiocodec_value, "pcma")) {
- JANUS_LOG(LOG_ERR, "Invalid element (audiocodec can only be opus, isac32, isac16, pcmu, or pcma)\n");
+ if(!strcasecmp(audiocodec_value, "opus") && !strcasecmp(audiocodec_value, "g722") &&
+ !strcasecmp(audiocodec_value, "isac32") && !strcasecmp(audiocodec_value, "isac16") &&
+ !strcasecmp(audiocodec_value, "pcmu") && !strcasecmp(audiocodec_value, "pcma")) {
+ JANUS_LOG(LOG_ERR, "Invalid element (audiocodec can only be opus, isac32, isac16, pcmu, pcma or g722)\n");
error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
- g_snprintf(error_cause, 512, "Invalid element (audiocodec can only be opus, isac32, isac16, pcmu, or pcma)");
+ g_snprintf(error_cause, 512, "Invalid element (audiocodec can only be opus, isac32, isac16, pcmu, pcma or g722)");
goto plugin_response;
}
}
@@ -1458,6 +1390,8 @@ struct janus_plugin_result *janus_videoroom_handle_message(janus_plugin_session
videoroom->acodec = JANUS_VIDEOROOM_PCMU;
else if(!strcasecmp(audiocodec_value, "pcma"))
videoroom->acodec = JANUS_VIDEOROOM_PCMA;
+ else if(!strcasecmp(audiocodec_value, "g722"))
+ videoroom->acodec = JANUS_VIDEOROOM_G722;
else {
JANUS_LOG(LOG_WARN, "Unsupported audio codec '%s', falling back to OPUS\n", audiocodec_value);
videoroom->acodec = JANUS_VIDEOROOM_OPUS;
@@ -2260,32 +2194,6 @@ void janus_videoroom_setup_media(janus_plugin_session *handle) {
}
}
}
- } else if(session->participant_type == janus_videoroom_p_type_subscriber_muxed) {
- /* Do the same, but for all feeds */
- janus_videoroom_listener_muxed *listener = (janus_videoroom_listener_muxed *)session->participant;
- if(listener == NULL)
- return;
- GSList *ps = listener->listeners;
- while(ps) {
- janus_videoroom_listener *l = (janus_videoroom_listener *)ps->data;
- if(l && l->feed) {
- janus_videoroom_participant *p = l->feed;
- if(p && p->session) {
- /* Send a FIR */
- char buf[20];
- memset(buf, 0, 20);
- janus_rtcp_fir((char *)&buf, 20, &p->fir_seq);
- JANUS_LOG(LOG_VERB, "New Multiplexed listener available, sending FIR to %"SCNu64" (%s)\n", p->user_id, p->display ? p->display : "??");
- gateway->relay_rtcp(p->session->handle, 1, buf, 20);
- /* Send a PLI too, just in case... */
- memset(buf, 0, 12);
- janus_rtcp_pli((char *)&buf, 12);
- JANUS_LOG(LOG_VERB, "New Multiplexed listener available, sending PLI to %"SCNu64" (%s)\n", p->user_id, p->display ? p->display : "??");
- gateway->relay_rtcp(p->session->handle, 1, buf, 12);
- }
- }
- ps = ps->next;
- }
}
}
}
@@ -2511,8 +2419,6 @@ void janus_videoroom_slow_link(janus_plugin_session *handle, int uplink, int vid
} else {
JANUS_LOG(LOG_WARN, "Got a slow downlink on a VideoRoom viewer? Weird, because it doesn't send media...\n");
}
- } else if(session->participant_type == janus_videoroom_p_type_subscriber_muxed) {
- /* TBD. */
}
}
@@ -2678,136 +2584,6 @@ void janus_videoroom_hangup_media(janus_plugin_session *handle) {
}
}
/* TODO Should we close the handle as well? */
- } else if(session->participant_type == janus_videoroom_p_type_subscriber_muxed) {
- /* Do the same, but for all sub-listener */
- janus_videoroom_listener_muxed *listener = (janus_videoroom_listener_muxed *)session->participant;
- GSList *ps = listener->listeners;
- while(ps) {
- janus_videoroom_listener *l = (janus_videoroom_listener *)ps->data;
- if(l) {
- l->paused = TRUE;
- janus_videoroom_participant *publisher = l->feed;
- if(publisher != NULL) {
- janus_mutex_lock(&publisher->listeners_mutex);
- publisher->listeners = g_slist_remove(publisher->listeners, l);
- janus_mutex_unlock(&publisher->listeners_mutex);
- l->feed = NULL;
- }
- }
- /* TODO Should we close the handle as well? */
- ps = ps->next;
- }
- /* TODO Should we close the handle as well? */
- }
-}
-
-static void janus_videoroom_sdp_a_format(char *mline, int mline_size, janus_videoroom_audiocodec acodec, int pt, const char *audio_mode, gboolean extmap, int extmap_id) {
- char audio_level_extmap[100];
- if(extmap) {
- /* We only negotiate support (if required) for a single audio extension, audio levels */
- g_snprintf(audio_level_extmap, sizeof(audio_level_extmap),
- "a=extmap:%d %s\r\n", extmap_id, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
- }
- switch(acodec) {
- case JANUS_VIDEOROOM_OPUS:
- g_snprintf(mline, mline_size, sdp_a_template_opus,
- pt, /* Opus payload type */
- audio_mode,
- pt, /* Opus payload type */
- extmap ? audio_level_extmap : "");
- break;
- case JANUS_VIDEOROOM_ISAC_32K:
- g_snprintf(mline, mline_size, sdp_a_template_isac32,
- pt, /* ISAC 32K payload type */
- audio_mode,
- pt, /* ISAC 32K payload type */
- extmap ? audio_level_extmap : "");
- break;
- case JANUS_VIDEOROOM_ISAC_16K:
- g_snprintf(mline, mline_size, sdp_a_template_isac16,
- pt, /* ISAC 16K payload type */
- audio_mode, /* The publisher gets a recvonly or inactive back */
- pt, /* ISAC 16K payload type */
- extmap ? audio_level_extmap : "");
- break;
- case JANUS_VIDEOROOM_PCMU:
- g_snprintf(mline, mline_size, sdp_a_template_pcmu,
- pt, /* PCMU payload type */
- audio_mode, /* The publisher gets a recvonly or inactive back */
- pt, /* PCMU payload type */
- extmap ? audio_level_extmap : "");
- break;
- case JANUS_VIDEOROOM_PCMA:
- g_snprintf(mline, mline_size, sdp_a_template_pcma,
- pt, /* PCMA payload type */
- audio_mode, /* The publisher gets a recvonly or inactive back */
- pt, /* PCMA payload type */
- extmap ? audio_level_extmap : "");
- break;
- default:
- /* Shouldn't happen */
- mline[0] = '\0';
- break;
- }
-}
-
-static void janus_videoroom_sdp_v_format(char *mline, int mline_size, janus_videoroom_videocodec vcodec, int pt, int b, const char *video_mode,
- gboolean vo_extmap, int vo_extmap_id, gboolean pd_extmap, int pd_extmap_id) {
- char extmaps[200], temp[100];
- memset(extmaps, 0, sizeof(extmaps));
- memset(temp, 0, sizeof(temp));
- if(vo_extmap) {
- g_snprintf(temp, sizeof(temp),
- "a=extmap:%d %s\r\n", vo_extmap_id, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION);
- g_strlcat(extmaps, temp, sizeof(extmaps));
- }
- if(pd_extmap) {
- g_snprintf(temp, sizeof(temp),
- "a=extmap:%d %s\r\n", pd_extmap_id, JANUS_RTP_EXTMAP_PLAYOUT_DELAY);
- g_strlcat(extmaps, temp, sizeof(extmaps));
- }
- switch(vcodec) {
- case JANUS_VIDEOROOM_VP8:
- g_snprintf(mline, mline_size, sdp_v_template_vp8,
- pt, /* payload type */
- b, /* Bandwidth */
- video_mode, /* The publisher gets a recvonly or inactive back */
- pt, /* payload type */
- pt, /* payload type */
- pt, /* payload type */
- pt, /* payload type */
- pt, /* payload type */
- (vo_extmap || pd_extmap) ? extmaps : "");
- break;
- case JANUS_VIDEOROOM_VP9:
- g_snprintf(mline, mline_size, sdp_v_template_vp9,
- pt, /* payload type */
- b, /* Bandwidth */
- video_mode, /* The publisher gets a recvonly or inactive back */
- pt, /* payload type */
- pt, /* payload type */
- pt, /* payload type */
- pt, /* payload type */
- pt, /* payload type */
- (vo_extmap || pd_extmap) ? extmaps : "");
- break;
- case JANUS_VIDEOROOM_H264:
- g_snprintf(mline, mline_size, sdp_v_template_h264,
- pt, /* payload type */
- b, /* Bandwidth */
- video_mode, /* The publisher gets a recvonly or inactive back */
- pt, /* payload type */
- pt, /* payload type */
- pt, /* payload type */
- pt, /* payload type */
- pt, /* payload type */
- pt, /* payload type */
- (vo_extmap || pd_extmap) ? extmaps : "");
- break;
- default:
- /* Shouldn't happen */
- mline[0] = '\0';
- break;
}
}
@@ -2982,6 +2758,9 @@ static void *janus_videoroom_handler(void *data) {
case JANUS_VIDEOROOM_PCMA:
publisher->audio_pt = PCMA_PT;
break;
+ case JANUS_VIDEOROOM_G722:
+ publisher->audio_pt = G722_PT;
+ break;
default:
/* Shouldn't happen */
publisher->audio_pt = OPUS_PT;
@@ -3145,7 +2924,6 @@ static void *janus_videoroom_handler(void *data) {
if(!publisher->data)
listener->data = FALSE; /* ... unless the publisher isn't sending any data */
listener->paused = TRUE; /* We need an explicit start from the listener */
- listener->parent = NULL;
session->participant = listener;
janus_mutex_lock(&publisher->listeners_mutex);
publisher->listeners = g_slist_append(publisher->listeners, listener);
@@ -3181,84 +2959,6 @@ static void *janus_videoroom_handler(void *data) {
continue;
}
}
- } else if(!strcasecmp(ptype_text, "muxed-listener")) {
- /* This is a new Multiplexed listener */
- JANUS_LOG(LOG_INFO, "Configuring new Multiplexed listener\n");
- /* Any feed we want to attach to already? */
- GList *list = NULL;
- JANUS_VALIDATE_JSON_OBJECT(root, feeds_parameters,
- error_code, error_cause, TRUE,
- JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
- if(error_code != 0)
- goto error;
- json_t *feeds = json_object_get(root, "feeds");
- if(feeds && json_array_size(feeds) > 0) {
- unsigned int i = 0;
- int problem = 0;
- for(i=0; i<json_array_size(feeds); i++) {
- if(videoroom->destroyed) {
- problem = 1;
- JANUS_LOG(LOG_ERR, "Room destroyed");
- error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
- g_snprintf(error_cause, 512, "Room destroyed");
- break;
- }
- json_t *feed = json_array_get(feeds, i);
- if(!feed || !json_is_integer(feed)) {
- problem = 1;
- JANUS_LOG(LOG_ERR, "Invalid element (feeds in the array must be integers)\n");
- error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
- g_snprintf(error_cause, 512, "Invalid element (feeds in the array must be integers)");
- break;
- }
- uint64_t feed_id = json_integer_value(feed);
- janus_mutex_lock(&videoroom->participants_mutex);
- janus_videoroom_participant *publisher = g_hash_table_lookup(videoroom->participants, &feed_id);
- janus_mutex_unlock(&videoroom->participants_mutex);
- if(publisher == NULL) { //~ || publisher->sdp == NULL) {
- /* FIXME For muxed listeners, we accept subscriptions to existing participants who haven't published yet */
- problem = 1;
- JANUS_LOG(LOG_ERR, "No such feed (%"SCNu64")\n", feed_id);
- error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
- g_snprintf(error_cause, 512, "No such feed (%"SCNu64")", feed_id);
- break;
- }
- list = g_list_prepend(list, GUINT_TO_POINTER(feed_id));
- JANUS_LOG(LOG_INFO, " -- Subscribing to feed %"SCNu64"\n", feed_id);
- }
- if(problem) {
- goto error;
- }
- }
- /* Allocate listener */
- janus_videoroom_listener_muxed *listener = g_malloc0(sizeof(janus_videoroom_listener_muxed));
- listener->session = session;
- listener->room = videoroom;
- session->participant_type = janus_videoroom_p_type_subscriber_muxed;
- session->participant = listener;
- /* Ack that we created the listener */
- event = json_object();
- json_object_set_new(event, "videoroom", json_string("muxed-created"));
- json_object_set_new(event, "room", json_integer(videoroom->room_id));
- JANUS_LOG(LOG_VERB, "Preparing JSON event as a reply\n");
- /* How long will the gateway take to push the event? */
- gint64 start = janus_get_monotonic_time();
- int res = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event, NULL);
- JANUS_LOG(LOG_VERB, " >> Pushing event: %d (took %"SCNu64" us)\n", res, janus_get_monotonic_time()-start);
- json_decref(event);
- /* Attach to feeds if needed */
- if(list != NULL) {
- JANUS_LOG(LOG_INFO, "Subscribing to %d feeds\n", g_list_length(list));
- list = g_list_reverse(list);
- if(videoroom->destroyed || janus_videoroom_muxed_subscribe(listener, list, msg->transaction) < 0) {
- JANUS_LOG(LOG_ERR, "Error subscribing!\n");
- error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR; /* FIXME */
- g_snprintf(error_cause, 512, "Error subscribing!");
- goto error;
- }
- }
- janus_videoroom_message_free(msg);
- continue;
} else {
JANUS_LOG(LOG_ERR, "Invalid element (ptype)\n");
error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
@@ -3635,164 +3335,6 @@ static void *janus_videoroom_handler(void *data) {
g_snprintf(error_cause, 512, "Unknown request '%s'", request_text);
goto error;
}
- } else if(session->participant_type == janus_videoroom_p_type_subscriber_muxed) {
- /* Handle this Multiplexed listener */
- janus_videoroom_listener_muxed *listener = (janus_videoroom_listener_muxed *)session->participant;
- if(listener == NULL) {
- JANUS_LOG(LOG_ERR, "Invalid Multiplexed listener instance\n");
- error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR;
- g_snprintf(error_cause, 512, "Invalid Multiplexed listener instance");
- goto error;
- }
- if(!strcasecmp(request_text, "join")) {
- JANUS_LOG(LOG_ERR, "Already in as a Multiplexed listener on this handle\n");
- error_code = JANUS_VIDEOROOM_ERROR_ALREADY_JOINED;
- g_snprintf(error_cause, 512, "Already in as a Multiplexed listener on this handle");
- goto error;
- } else if(!strcasecmp(request_text, "add")) {
- /* Add new streams to subscribe to */
- GList *list = NULL;
- JANUS_VALIDATE_JSON_OBJECT(root, feeds_parameters,
- error_code, error_cause, TRUE,
- JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
- if(error_code != 0)
- goto error;
- json_t *feeds = json_object_get(root, "feeds");
- unsigned int i = 0;
- int problem = 0;
- if(!listener->room) {
- JANUS_LOG(LOG_ERR, "Room Destroyed ");
- error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
- g_snprintf(error_cause, 512, "No such room ");
- goto error;
- }
- if(listener->room->destroyed) {
- JANUS_LOG(LOG_ERR, "Room Destroyed (%"SCNu64")", listener->room->room_id);
- error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
- g_snprintf(error_cause, 512, "No such room (%"SCNu64")", listener->room->room_id);
- goto error;
- }
- for(i=0; i<json_array_size(feeds); i++) {
- json_t *feed = json_array_get(feeds, i);
- if(listener->room->destroyed) {
- problem = 1;
- JANUS_LOG(LOG_ERR, "Room destroyed");
- error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_ROOM;
- g_snprintf(error_cause, 512, "Room destroyed");
- break;
- }
- if(!feed || !json_is_integer(feed)) {
- problem = 1;
- JANUS_LOG(LOG_ERR, "Invalid element (feeds in the array must be integers)\n");
- error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
- g_snprintf(error_cause, 512, "Invalid element (feeds in the array must be integers)");
- break;
- }
- uint64_t feed_id = json_integer_value(feed);
- janus_mutex_lock(&listener->room->participants_mutex);
- janus_videoroom_participant *publisher = g_hash_table_lookup(listener->room->participants, &feed_id);
- janus_mutex_unlock(&listener->room->participants_mutex);
- if(publisher == NULL) { //~ || publisher->sdp == NULL) {
- /* FIXME For muxed listeners, we accept subscriptions to existing participants who haven't published yet */
- problem = 1;
- JANUS_LOG(LOG_ERR, "No such feed (%"SCNu64")\n", feed_id);
- error_code = JANUS_VIDEOROOM_ERROR_NO_SUCH_FEED;
- g_snprintf(error_cause, 512, "No such feed (%"SCNu64")", feed_id);
- break;
- }
- list = g_list_prepend(list, GUINT_TO_POINTER(feed_id));
- }
- if(problem) {
- goto error;
- }
- list = g_list_reverse(list);
- if(janus_videoroom_muxed_subscribe(listener, list, msg->transaction) < 0) {
- JANUS_LOG(LOG_ERR, "Error subscribing!\n");
- error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR; /* FIXME */
- g_snprintf(error_cause, 512, "Error subscribing!");
- goto error;
- }
- janus_videoroom_message_free(msg);
- continue;
- } else if(!strcasecmp(request_text, "remove")) {
- /* Remove subscribed streams */
- GList *list = NULL;
- JANUS_VALIDATE_JSON_OBJECT(root, feeds_parameters,
- error_code, error_cause, TRUE,
- JANUS_VIDEOROOM_ERROR_MISSING_ELEMENT, JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT);
- if(error_code != 0)
- goto error;
- json_t *feeds = json_object_get(root, "feeds");
- unsigned int i = 0;
- int error = 0;
- for(i=0; i<json_array_size(feeds); i++) {
- json_t *feed = json_array_get(feeds, i);
- if(!feed || !json_is_integer(feed)) {
- error = 1;
- break;
- }
- list = g_list_prepend(list, GUINT_TO_POINTER(json_integer_value(feed)));
- }
- if(error) {
- JANUS_LOG(LOG_ERR, "Invalid element (feeds in the array must be integers)\n");
- error_code = JANUS_VIDEOROOM_ERROR_INVALID_ELEMENT;
- g_snprintf(error_cause, 512, "Invalid element (feeds in the array must be integers)");
- goto error;
- }
- list = g_list_reverse(list);
-
- if(!listener->room) {
- JANUS_LOG(LOG_ERR, "Error unsubscribing!\n");
- error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR; /* FIXME */
- g_snprintf(error_cause, 512, "Error unsubscribing!");
- goto error;
- }
- if(janus_videoroom_muxed_unsubscribe(listener, list, msg->transaction) < 0) {
- JANUS_LOG(LOG_ERR, "Error unsubscribing!\n");
- error_code = JANUS_VIDEOROOM_ERROR_UNKNOWN_ERROR; /* FIXME */
- g_snprintf(error_cause, 512, "Error unsubscribing!");
- goto error;
- }
- janus_videoroom_message_free(msg);
- continue;
- } else if(!strcasecmp(request_text, "start")) {
- /* Start/restart receiving the publishers streams */
- /* TODO */
- event = json_object();
- json_object_set_new(event, "videoroom", json_string("event"));
- json_object_set_new(event, "room", json_integer(listener->room->room_id));
- json_object_set_new(event, "started", json_string("ok"));
- //~ /* Send a FIR */
- //~ char buf[20];
- //~ memset(buf, 0, 20);
- //~ janus_rtcp_fir((char *)&buf, 20, &publisher->fir_seq);
- //~ JANUS_LOG(LOG_VERB, "Resuming publisher, sending FIR to %"SCNu64" (%s)\n", publisher->user_id, publisher->display ? publisher->display : "??");
- //~ gateway->relay_rtcp(publisher->session->handle, 1, buf, 20);
- //~ /* Send a PLI too, just in case... */
- //~ memset(buf, 0, 12);
- //~ janus_rtcp_pli((char *)&buf, 12);
- //~ JANUS_LOG(LOG_VERB, "Resuming publisher, sending PLI to %"SCNu64" (%s)\n", publisher->user_id, publisher->display ? publisher->display : "??");
- //~ gateway->relay_rtcp(publisher->session->handle, 1, buf, 12);
- } else if(!strcasecmp(request_text, "pause")) {
- /* Stop receiving the publishers streams for a while */
- /* TODO */
- event = json_object();
- json_object_set_new(event, "videoroom", json_string("event"));
- json_object_set_new(event, "room", json_integer(listener->room->room_id));
- json_object_set_new(event, "paused", json_string("ok"));
- } else if(!strcasecmp(request_text, "leave")) {
- /* TODO */
- event = json_object();
- json_object_set_new(event, "videoroom", json_string("event"));
- json_object_set_new(event, "room", json_integer(listener->room->room_id));
- json_object_set_new(event, "left", json_string("ok"));
- session->started = FALSE;
- } else {
- JANUS_LOG(LOG_ERR, "Unknown request '%s'\n", request_text);
- error_code = JANUS_VIDEOROOM_ERROR_INVALID_REQUEST;
- g_snprintf(error_cause, 512, "Unknown request '%s'", request_text);
- goto error;
- }
}
/* Prepare JSON event */
@@ -3867,67 +3409,29 @@ static void *janus_videoroom_handler(void *data) {
if(strstr(msg_sdp, "Mozilla")) {
participant->firefox = TRUE;
}
- /* Which media are available? */
- int audio = 0, video = 0, data = 0;
- const char *audio_mode = NULL, *video_mode = NULL;
- gboolean audio_level_extmap = FALSE, video_orient_extmap = FALSE, playout_delay_extmap = FALSE;
- char error_str[100];
- janus_sdp *parsed_sdp = janus_sdp_parse(msg_sdp, error_str, sizeof(error_str));
- if(!parsed_sdp) {
- /* Invalid SDP */
- JANUS_LOG(LOG_ERR, "Error parsing SDP: %s\n", error_str);
- error_code = JANUS_VIDEOROOM_ERROR_PUBLISHERS_FULL;
- g_snprintf(error_cause, 512, "Error parsing SDP: %s", error_str);
+ /* Start by parsing the offer */
+ char error_str[512];
+ janus_sdp *offer = janus_sdp_parse(msg_sdp, error_str, sizeof(error_str));
+ if(offer == NULL) {
+ json_decref(event);
+ JANUS_LOG(LOG_ERR, "Error parsing offer: %s\n", error_str);
+ error_code = JANUS_VIDEOROOM_ERROR_INVALID_SDP;
+ g_snprintf(error_cause, 512, "Error parsing offer: %s", error_str);
goto error;
}
- GList *temp = parsed_sdp->m_lines;
+ gboolean audio_level_extmap = FALSE, video_orient_extmap = FALSE, playout_delay_extmap = FALSE;
+ GList *temp = offer->m_lines;
while(temp) {
+ /* Which media are available? */
janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
- audio++;
participant->audio = TRUE;
- if(audio > 1) {
- temp = temp->next;
- continue;
- }
} else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
- video++;
participant->video = TRUE;
- if(video > 1) {
- temp = temp->next;
- continue;
- }
} else if(m->type == JANUS_SDP_APPLICATION && m->port > 0) {
- data++;
participant->data = TRUE;
- if(data > 1) {
- temp = temp->next;
- continue;
- }
}
if(m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO) {
- /* What is the direction? */
- switch(m->direction) {
- case JANUS_SDP_RECVONLY:
- /* If we're getting a 'recvonly' publisher, we're going to answer with 'inactive' */
- case JANUS_SDP_INACTIVE:
- if(m->type == JANUS_SDP_AUDIO) {
- audio_mode = "inactive";
- } else {
- video_mode = "inactive";
- }
- break;
- case JANUS_SDP_SENDONLY:
- /* What we expect, turn this into 'recvonly' */
- case JANUS_SDP_SENDRECV:
- default:
- if(m->type == JANUS_SDP_AUDIO) {
- audio_mode = "recvonly";
- } else {
- video_mode = "recvonly";
- }
- break;
- }
/* Are the extmaps we care about there? */
GList *ma = m->attributes;
while(ma) {
@@ -3949,185 +3453,123 @@ static void *janus_videoroom_handler(void *data) {
}
temp = temp->next;
}
- janus_sdp_free(parsed_sdp);
- JANUS_LOG(LOG_VERB, "The publisher %s going to send an audio stream\n", audio ? "is" : "is NOT");
- int opus_pt = 0, isac32_pt = 0, isac16_pt = 0, pcmu_pt = 0, pcma_pt = 0,
- vp8_pt = 0, vp9_pt = 0, h264_pt = 0;
- if(audio) {
- JANUS_LOG(LOG_VERB, " -- Will answer with media direction '%s'\n", audio_mode);
- opus_pt = janus_get_codec_pt(msg_sdp, "opus");
- if(opus_pt > 0) {
- JANUS_LOG(LOG_VERB, " -- -- Opus payload type is %d\n", opus_pt);
- }
- isac32_pt = janus_get_codec_pt(msg_sdp, "isac32");
- if(isac32_pt > 0) {
- JANUS_LOG(LOG_VERB, " -- -- ISAC 32K payload type is %d\n", isac32_pt);
- }
- isac16_pt = janus_get_codec_pt(msg_sdp, "isac16");
- if(isac16_pt > 0) {
- JANUS_LOG(LOG_VERB, " -- -- ISAC 16K payload type is %d\n", isac16_pt);
- }
- pcmu_pt = janus_get_codec_pt(msg_sdp, "pcmu");
- if(pcmu_pt > 0) {
- JANUS_LOG(LOG_VERB, " -- -- PCMU payload type is %d\n", pcmu_pt);
- }
- pcma_pt = janus_get_codec_pt(msg_sdp, "pcma");
- if(pcma_pt > 0) {
- JANUS_LOG(LOG_VERB, " -- -- PCMA payload type is %d\n", pcma_pt);
- }
- }
- JANUS_LOG(LOG_VERB, "The publisher %s going to send a video stream\n", video ? "is" : "is NOT");
- if(video) {
- JANUS_LOG(LOG_VERB, " -- Will answer with media direction '%s'\n", video_mode);
- vp8_pt = janus_get_codec_pt(msg_sdp, "vp8");
- if(vp8_pt > 0) {
- JANUS_LOG(LOG_VERB, " -- -- VP8 payload type is %d\n", vp8_pt);
- }
- vp9_pt = janus_get_codec_pt(msg_sdp, "vp9");
- if(vp9_pt > 0) {
- JANUS_LOG(LOG_VERB, " -- -- VP9 payload type is %d\n", vp9_pt);
- }
- h264_pt = janus_get_codec_pt(msg_sdp, "h264");
- if(h264_pt > 0) {
- JANUS_LOG(LOG_VERB, " -- -- H264 payload type is %d\n", h264_pt);
+ /* Prepare an answer now: force the room codecs and recvonly on the Janus side */
+ JANUS_LOG(LOG_VERB, "The publisher %s going to send an audio stream\n", participant->audio ? "is" : "is NOT");
+ JANUS_LOG(LOG_VERB, "The publisher %s going to send a video stream\n", participant->video ? "is" : "is NOT");
+ JANUS_LOG(LOG_VERB, "The publisher %s going to open a data channel\n", participant->data ? "is" : "is NOT");
+ janus_sdp *answer = janus_sdp_generate_answer(offer,
+ JANUS_SDP_OA_AUDIO_CODEC, janus_videoroom_audiocodec_name(videoroom->acodec),
+ JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_RECVONLY,
+ JANUS_SDP_OA_VIDEO_CODEC, janus_videoroom_videocodec_name(videoroom->vcodec),
+ JANUS_SDP_OA_VIDEO_DIRECTION, JANUS_SDP_RECVONLY,
+ JANUS_SDP_OA_DONE);
+ janus_sdp_free(offer);
+ /* Replace the session name */
+ g_free(answer->s_name);
+ answer->s_name = g_strdup(videoroom->room_name);
+ /* Which media are REALLY available? (some may have been rejected) */
+ participant->audio = FALSE;
+ participant->video = FALSE;
+ participant->data = FALSE;
+ temp = answer->m_lines;
+ while(temp) {
+ janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
+ if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
+ participant->audio = TRUE;
+ } else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
+ participant->video = TRUE;
+ } else if(m->type == JANUS_SDP_APPLICATION && m->port > 0) {
+ participant->data = TRUE;
}
+ temp = temp->next;
}
- JANUS_LOG(LOG_VERB, "The publisher %s going to open a data channel\n", data ? "is" : "is NOT");
+ JANUS_LOG(LOG_VERB, "Per the answer, the publisher %s going to send an audio stream\n", participant->audio ? "is" : "is NOT");
+ JANUS_LOG(LOG_VERB, "Per the answer, the publisher %s going to send a video stream\n", participant->video ? "is" : "is NOT");
+ JANUS_LOG(LOG_VERB, "Per the answer, the publisher %s going to open a data channel\n", participant->data ? "is" : "is NOT");
/* Also add a bandwidth SDP attribute if we're capping the bitrate in the room */
- int b = 0;
- if(participant->firefox) /* Don't add any b=AS attribute for Chrome */
- b = (int)(videoroom->bitrate/1000);
- char sdp[1280], audio_mline[256], video_mline[512], data_mline[256];
- char *newsdp = NULL;
- int res = 0;
- int pass = 0;
- for(pass = 1; pass <= 2; pass++) {
- if(pass == 2) {
- /* Now turn the SDP into what we'll send subscribers, using the static payload types for making switching easier */
- if(audio_mode && strcmp(audio_mode, "inactive"))
- /* The publisher gets a recvonly or inactive back */
- /* Subscribers gets a sendonly or inactive back */
- audio_mode = "sendonly";
- if(video_mode && strcmp(video_mode, "inactive"))
- video_mode = "sendonly";
- }
- audio_mline[0] = '\0';
- if(audio) {
- int pt = -1;
- switch(videoroom->acodec) {
- case JANUS_VIDEOROOM_OPUS:
- if(opus_pt >= 0)
- pt = (pass == 1 ? opus_pt : OPUS_PT);
- break;
- case JANUS_VIDEOROOM_ISAC_32K:
- if(isac32_pt >= 0)
- pt = (pass == 1 ? isac32_pt : ISAC32_PT);
- break;
- case JANUS_VIDEOROOM_ISAC_16K:
- if(isac16_pt >= 0)
- pt = (pass == 1 ? isac16_pt : ISAC16_PT);
- break;
- case JANUS_VIDEOROOM_PCMU:
- if(pcmu_pt >= 0)
- pt = (pass == 1 ? pcmu_pt : PCMU_PT);
- break;
- case JANUS_VIDEOROOM_PCMA:
- if(pcma_pt >= 0)
- pt = (pass == 1 ? pcma_pt : PCMA_PT);
- break;
- default:
- /* Shouldn't happen */
- break;
- }
- if(pass == 1 && pt < 0)
- JANUS_LOG(LOG_WARN, "Videoroom is forcing %s, but publisher didn't offer any... rejecting audio\n", janus_videoroom_audiocodec_name(videoroom->acodec));
- if(pt >= 0) {
- janus_videoroom_sdp_a_format(audio_mline, 256, videoroom->acodec, pt, audio_mode,
- audio_level_extmap, participant->audio_level_extmap_id);
- }
- if(audio_mline[0] == '\0' && pass == 1) {
- /* Remove "pass == 1" if the listener also should get a line with port=0. */
- g_snprintf(audio_mline, 256, "m=audio 0 RTP/SAVPF 0\r\n");
- }
- }
- video_mline[0] = '\0';
- if(video) {
- int pt = -1;
- switch(videoroom->vcodec) {
- case JANUS_VIDEOROOM_VP8:
- if(vp8_pt >= 0)
- pt = (pass == 1 ? vp8_pt : VP8_PT);
- break;
- case JANUS_VIDEOROOM_VP9:
- if(vp9_pt >= 0)
- pt = (pass == 1 ? vp9_pt : VP9_PT);
- break;
- case JANUS_VIDEOROOM_H264:
- if(h264_pt >= 0)
- pt = (pass == 1 ? h264_pt : H264_PT);
- break;
- default:
- /* Shouldn't happen */
- break;
- }
- if(pass == 1 && pt < 0)
- JANUS_LOG(LOG_WARN, "Videoroom is forcing %s, but publisher didn't offer any... rejecting video\n", janus_videoroom_videocodec_name(videoroom->vcodec));
- if(pt >= 0) {
- janus_videoroom_sdp_v_format(video_mline, 512, videoroom->vcodec, pt, b,video_mode,
- video_orient_extmap, participant->video_orient_extmap_id,
- playout_delay_extmap, participant->playout_delay_extmap_id);
- }
- if(video_mline[0] == '\0' && pass == 1) {
- /* Remove "pass == 1" if the listener also should get a line with port=0. */
- g_snprintf(video_mline, 512, "m=video 0 RTP/SAVPF 0\r\n");
- }
- }
- if(data) {
- g_snprintf(data_mline, 256, sdp_d_template);
- } else {
- data_mline[0] = '\0';
- }
- g_snprintf(sdp, 1280, sdp_template,
- janus_get_real_time(), /* We need current time here */
- janus_get_real_time(), /* We need current time here */
- participant->room->room_name, /* Video room name */
- audio_mline, /* Audio m-line, if any */
- video_mline, /* Video m-line, if any */
- data_mline); /* Data channel m-line, if any */
- newsdp = g_strdup(sdp);
- if(video && b == 0) {
- /* Remove useless bandwidth attribute */
- newsdp = janus_string_replace(newsdp, "b=AS:0\r\n", "");
- }
- if(pass == 2)
- break;
- /* Is this room recorded? */
- janus_mutex_lock(&participant->rec_mutex);
- if(videoroom->record || participant->recording_active) {
- janus_videoroom_recorder_create(participant, audio, video, data);
+ if(participant->firefox) { /* Don't add any b=AS attribute for Chrome */
+ janus_sdp_mline *m = janus_sdp_mline_find(answer, JANUS_SDP_VIDEO);
+ if(m != NULL && videoroom->bitrate > 0) {
+ m->b_name = g_strdup("AS");
+ m->b_value = (int)(videoroom->bitrate/1000);
}
- janus_mutex_unlock(&participant->rec_mutex);
-
- JANUS_LOG(LOG_VERB, "Handling publisher: turned this into an '%s':\n%s\n", type, newsdp);
- json_t *jsep = json_pack("{ssss}", "type", type, "sdp", newsdp);
- /* How long will the gateway take to push the event? */
- g_atomic_int_set(&session->hangingup, 0);
- gint64 start = janus_get_monotonic_time();
- res = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event, jsep);
- JANUS_LOG(LOG_VERB, " >> Pushing event: %d (took %"SCNu64" us)\n", res, janus_get_monotonic_time()-start);
- json_decref(event);
- json_decref(jsep);
- g_free(newsdp);
}
+ /* Add the extmap attributes, if needed */
+ if(audio_level_extmap) {
+ janus_sdp_attribute *a = janus_sdp_attribute_create("extmap",
+ "%d %s\r\n", participant->audio_level_extmap_id, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
+ janus_sdp_attribute_add_to_mline(janus_sdp_mline_find(answer, JANUS_SDP_AUDIO), a);
+ }
+ if(video_orient_extmap) {
+ janus_sdp_attribute *a = janus_sdp_attribute_create("extmap",
+ "%d %s\r\n", participant->video_orient_extmap_id, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION);
+ janus_sdp_attribute_add_to_mline(janus_sdp_mline_find(answer, JANUS_SDP_VIDEO), a);
+ }
+ if(playout_delay_extmap) {
+ janus_sdp_attribute *a = janus_sdp_attribute_create("extmap",
+ "%d %s\r\n", participant->playout_delay_extmap_id, JANUS_RTP_EXTMAP_PLAYOUT_DELAY);
+ janus_sdp_attribute_add_to_mline(janus_sdp_mline_find(answer, JANUS_SDP_VIDEO), a);
+ }
+ /* Generate an SDP string we can send back to the publisher */
+ char *answer_sdp = janus_sdp_write(answer);
+ /* Now turn the SDP into what we'll send subscribers, using the static payload types for making switching easier */
+ offer = janus_sdp_generate_offer(videoroom->room_name, answer->c_addr,
+ JANUS_SDP_OA_AUDIO, participant->audio,
+ JANUS_SDP_OA_AUDIO_CODEC, janus_videoroom_audiocodec_name(videoroom->acodec),
+ JANUS_SDP_OA_AUDIO_PT, janus_videoroom_audiocodec_pt(videoroom->acodec),
+ JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_SENDONLY,
+ JANUS_SDP_OA_VIDEO, participant->video,
+ JANUS_SDP_OA_VIDEO_CODEC, janus_videoroom_videocodec_name(videoroom->vcodec),
+ JANUS_SDP_OA_VIDEO_PT, janus_videoroom_videocodec_pt(videoroom->vcodec),
+ JANUS_SDP_OA_VIDEO_DIRECTION, JANUS_SDP_SENDONLY,
+ JANUS_SDP_OA_DATA, participant->data,
+ JANUS_SDP_OA_DONE);
+ /* Add the extmap attributes, if needed */
+ if(audio_level_extmap) {
+ janus_sdp_attribute *a = janus_sdp_attribute_create("extmap",
+ "%d %s\r\n", participant->audio_level_extmap_id, JANUS_RTP_EXTMAP_AUDIO_LEVEL);
+ janus_sdp_attribute_add_to_mline(janus_sdp_mline_find(offer, JANUS_SDP_AUDIO), a);
+ }
+ if(video_orient_extmap) {
+ janus_sdp_attribute *a = janus_sdp_attribute_create("extmap",
+ "%d %s\r\n", participant->video_orient_extmap_id, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION);
+ janus_sdp_attribute_add_to_mline(janus_sdp_mline_find(offer, JANUS_SDP_VIDEO), a);
+ }
+ if(playout_delay_extmap) {
+ janus_sdp_attribute *a = janus_sdp_attribute_create("extmap",
+ "%d %s\r\n", participant->playout_delay_extmap_id, JANUS_RTP_EXTMAP_PLAYOUT_DELAY);
+ janus_sdp_attribute_add_to_mline(janus_sdp_mline_find(offer, JANUS_SDP_VIDEO), a);
+ }
+ /* Generate an SDP string we can offer subscribers later on */
+ char *offer_sdp = janus_sdp_write(offer);
+ janus_sdp_free(offer);
+ janus_sdp_free(answer);
+ /* Is this room recorded? */
+ janus_mutex_lock(&participant->rec_mutex);
+ if(videoroom->record || participant->recording_active) {
+ janus_videoroom_recorder_create(participant, participant->audio, participant->video, participant->data);
+ }
+ janus_mutex_unlock(&participant->rec_mutex);
+ /* Send the answer back to the publisher */
+ JANUS_LOG(LOG_VERB, "Handling publisher: turned this into an '%s':\n%s\n", type, answer_sdp);
+ json_t *jsep = json_pack("{ssss}", "type", type, "sdp", answer_sdp);
+ g_free(answer_sdp);
+ /* How long will the gateway take to push the event? */
+ g_atomic_int_set(&session->hangingup, 0);
+ gint64 start = janus_get_monotonic_time();
+ int res = gateway->push_event(msg->handle, &janus_videoroom_plugin, msg->transaction, event, jsep);
+ JANUS_LOG(LOG_VERB, " >> Pushing event: %d (took %"SCNu64" us)\n", res, janus_get_monotonic_time()-start);
/* Done */
if(res != JANUS_OK) {
/* TODO Failed to negotiate? We should remove this publisher */
- g_free(newsdp);
+ g_free(offer_sdp);
} else {
/* Store the participant's SDP for interested listeners */
- participant->sdp = newsdp;
+ participant->sdp = offer_sdp;
/* We'll wait for the setup_media event before actually telling listeners */
}
+ json_decref(event);
+ json_decref(jsep);
}
}
janus_videoroom_message_free(msg);
@@ -4152,238 +3594,6 @@ error:
}
-/* Multiplexing helpers */
-int janus_videoroom_muxed_subscribe(janus_videoroom_listener_muxed *muxed_listener, GList *feeds, char *transaction) {
- if(!muxed_listener || !feeds)
- return -1;
- janus_mutex_lock(&muxed_listener->listeners_mutex);
- JANUS_LOG(LOG_VERB, "Subscribing to %d feeds\n", g_list_length(feeds));
- janus_videoroom *videoroom = muxed_listener->room;
- GList *ps = feeds;
- json_t *list = json_array();
- int added_feeds = 0;
- while(ps) {
- uint64_t feed_id = GPOINTER_TO_UINT(ps->data);
- janus_videoroom_participant *publisher = g_hash_table_lookup(videoroom->participants, &feed_id);
- if(publisher == NULL) { //~ || publisher->sdp == NULL) {
- /* FIXME For muxed listeners, we accept subscriptions to existing participants who haven't published yet */
- JANUS_LOG(LOG_WARN, "No such feed (%"SCNu64"), skipping\n", feed_id);
- ps = ps->next;
- continue;
- }
- /* Are we already subscribed? */
- gboolean subscribed = FALSE;
- GSList *ls = muxed_listener->listeners;
- while(ls) {
- janus_videoroom_listener *l = (janus_videoroom_listener *)ls->data;
- if(l && (l->feed == publisher)) {
- subscribed = TRUE;
- JANUS_LOG(LOG_WARN, "Already subscribed to feed %"SCNu64", skipping\n", feed_id);
- break;
- }
- ls = ls->next;
- }
- if(subscribed) {
- ps = ps->next;
- continue;
- }
- janus_videoroom_listener *listener = g_malloc0(sizeof(janus_videoroom_listener));
- listener->session = muxed_listener->session;
- listener->room = videoroom;
- listener->feed = publisher;
- //~ listener->paused = TRUE; /* We need an explicit start from the listener */
- listener->paused = FALSE;
- listener->parent = muxed_listener;
- janus_mutex_lock(&publisher->listeners_mutex);
- publisher->listeners = g_slist_append(publisher->listeners, listener);
- janus_mutex_unlock(&publisher->listeners_mutex);
- muxed_listener->listeners = g_slist_append(muxed_listener->listeners, listener);
- JANUS_LOG(LOG_VERB, "Now subscribed to %d feeds\n", g_slist_length(muxed_listener->listeners));
- /* Add to feeds in the answer */
- added_feeds++;
- json_t *f = json_object();
- json_object_set_new(f, "id", json_integer(feed_id));
- if(publisher->display)
- json_object_set_new(f, "display", json_string(publisher->display));
- json_array_append_new(list, f);
- ps = ps->next;
- }
- janus_mutex_unlock(&muxed_listener->listeners_mutex);
- if(added_feeds == 0) {
- /* Nothing changed */
- return 0;
- }
- /* Prepare event */
- json_t *event = json_object();
- json_object_set_new(event, "videoroom", json_string("muxed-attached"));
- json_object_set_new(event, "room", json_integer(videoroom->room_id));
- json_object_set_new(event, "feeds", list);
- JANUS_LOG(LOG_VERB, "Preparing JSON event as a reply\n");
- /* Send the updated offer */
- return janus_videoroom_muxed_offer(muxed_listener, transaction, event);
-}
-
-int janus_videoroom_muxed_unsubscribe(janus_videoroom_listener_muxed *muxed_listener, GList *feeds, char *transaction) {
- janus_mutex_lock(&muxed_listener->listeners_mutex);
- JANUS_LOG(LOG_VERB, "Unsubscribing from %d feeds\n", g_list_length(feeds));
- janus_videoroom *videoroom = muxed_listener->room;
- GList *ps = feeds;
- json_t *list = json_array();
- int removed_feeds = 0;
- while(ps) {
- uint64_t feed_id = GPOINTER_TO_UINT(ps->data);
- GSList *ls = muxed_listener->listeners;
- while(ls) {
- janus_videoroom_listener *listener = (janus_videoroom_listener *)ls->data;
- if(listener) {
- janus_videoroom_participant *publisher = listener->feed;
- if(publisher == NULL || publisher->user_id != feed_id) {
- /* Not the publisher we're looking for */
- ls = ls->next;
- continue;
- }
- janus_mutex_lock(&publisher->listeners_mutex);
- publisher->listeners = g_slist_remove(publisher->listeners, listener);
- janus_mutex_unlock(&publisher->listeners_mutex);
- listener->feed = NULL;
- muxed_listener->listeners = g_slist_remove(muxed_listener->listeners, listener);
- JANUS_LOG(LOG_VERB, "Now subscribed to %d feeds\n", g_slist_length(muxed_listener->listeners));
- janus_videoroom_listener_free(listener);
- /* Add to feeds in the answer */
- removed_feeds++;
- json_t *f = json_object();
- json_object_set_new(f, "id", json_integer(feed_id));
- json_array_append_new(list, f);
- break;
- }
- ls = ls->next;
- }
- ps = ps->next;
- }
- janus_mutex_unlock(&muxed_listener->listeners_mutex);
- if(removed_feeds == 0) {
- /* Nothing changed */
- return 0;
- }
- /* Prepare event */
- json_t *event = json_object();
- json_object_set_new(event, "videoroom", json_string("muxed-detached"));
- json_object_set_new(event, "room", json_integer(videoroom->room_id));
- json_object_set_new(event, "feeds", list);
- JANUS_LOG(LOG_VERB, "Preparing JSON event as a reply\n");
- /* Send the updated offer */
- return janus_videoroom_muxed_offer(muxed_listener, transaction, event);
-}
-
-int janus_videoroom_muxed_offer(janus_videoroom_listener_muxed *muxed_listener, char *transaction, json_t *event) {
- if(muxed_listener == NULL)
- return -1;
- /* Negotiate by placing a 'muxed' fake attribute for each publisher we subscribed to,
- * that will translate to multiple SSRCs when merging the SDP */
- int audio = 0, video = 0;
- char audio_muxed[1024], video_muxed[1024], temp[255];
- char sdp[2048], audio_mline[512], video_mline[512], data_mline[1];
- data_mline[0] = '\0'; /* Multiplexed streams do not support data channels */
- memset(audio_muxed, 0, 1024);
- memset(video_muxed, 0, 1024);
- memset(audio_mline, 0, 512);
- memset(video_mline, 0, 512);
- /* Prepare the m-lines (FIXME this will result in an audio line even for video-only rooms, but we don't care) */
- int pt = -1;
- switch(muxed_listener->room->acodec) {
- case JANUS_VIDEOROOM_OPUS:
- pt = OPUS_PT;
- break;
- case JANUS_VIDEOROOM_ISAC_32K:
- pt = ISAC32_PT;
- break;
- case JANUS_VIDEOROOM_ISAC_16K:
- pt = ISAC16_PT;
- break;
- case JANUS_VIDEOROOM_PCMU:
- pt = PCMU_PT;
- break;
- case JANUS_VIDEOROOM_PCMA:
- pt = PCMA_PT;
- break;
- default:
- /* Shouldn't happen */
- break;
- }
- janus_videoroom_sdp_a_format(audio_mline, 512, muxed_listener->room->acodec, pt, "sendonly", FALSE, 0);
- pt = -1;
- switch(muxed_listener->room->vcodec) {
- case JANUS_VIDEOROOM_VP8:
- pt = VP8_PT;
- break;
- case JANUS_VIDEOROOM_VP9:
- pt = VP9_PT;
- break;
- case JANUS_VIDEOROOM_H264:
- pt = H264_PT;
- break;
- default:
- /* Shouldn't happen */
- break;
- }
- janus_videoroom_sdp_v_format(video_mline, 512, muxed_listener->room->vcodec, pt, 0, "sendonly", FALSE, 0, FALSE, 0);
- /* FIXME Add a fake user/SSRC just to avoid the "Failed to set max send bandwidth for video content" bug */
- g_strlcat(audio_muxed, "a=planb:sfu0 1\r\n", 1024);
- g_strlcat(video_muxed, "a=planb:sfu0 2\r\n", 1024);
- /* Go through all the available publishers */
- GSList *ps = muxed_listener->listeners;
- while(ps) {
- janus_videoroom_listener *l = (janus_videoroom_listener *)ps->data;
- if(l && l->feed) { //~ && l->feed->sdp) {
- if(strstr(l->feed->sdp, "m=audio")) {
- audio++;
- g_snprintf(temp, 255, "a=planb:sfu%"SCNu64" %"SCNu32"\r\n", l->feed->user_id, l->feed->audio_ssrc);
- g_strlcat(audio_muxed, temp, 1024);
- }
- if(strstr(l->feed->sdp, "m=video")) {
- video++;
- g_snprintf(temp, 255, "a=planb:sfu%"SCNu64" %"SCNu32"\r\n", l->feed->user_id, l->feed->video_ssrc);
- g_strlcat(video_muxed, temp, 1024);
- }
- }
- ps = ps->next;
- }
- /* Also add a bandwidth SDP attribute if we're capping the bitrate in the room */
- if(audio) {
- g_strlcat(audio_mline, audio_muxed, 2048);
- }
- if(video) {
- g_strlcat(video_mline, video_muxed, 2048);
- }
- g_snprintf(sdp, 2048, sdp_template,
- janus_get_real_time(), /* We need current time here */
- janus_get_real_time(), /* We need current time here */
- muxed_listener->room->room_name, /* Video room name */
- audio_mline, /* Audio m-line */
- video_mline, /* Video m-line */
- data_mline); /* Data channel m-line */
- char *newsdp = g_strdup(sdp);
- if(video) {
- /* Remove useless bandwidth attribute, if any */
- newsdp = janus_string_replace(newsdp, "b=AS:0\r\n", "");
- }
- JANUS_LOG(LOG_VERB, "%s", newsdp);
- json_t *jsep = json_pack("{ssss}", "type", "offer", "sdp", newsdp);
- /* How long will the gateway take to push the event? */
- gint64 start = janus_get_monotonic_time();
- int res = gateway->push_event(muxed_listener->session->handle, &janus_videoroom_plugin, transaction, event, jsep);
- JANUS_LOG(LOG_VERB, " >> Pushing event: %d (took %"SCNu64" us)\n", res, janus_get_monotonic_time()-start);
- json_decref(event);
- json_decref(jsep);
- if(res != JANUS_OK) {
- /* TODO Failed to negotiate? We should remove this listener */
- } else {
- /* Let's wait for the setup_media event */
- }
- return 0;
-}
-
-
/* Helper to quickly relay RTP packets from publishers to subscribers */
static void janus_videoroom_relay_rtp_packet(gpointer data, gpointer user_data) {
janus_videoroom_rtp_relay_packet *packet = (janus_videoroom_rtp_relay_packet *)user_data;
@@ -4523,20 +3733,6 @@ static void janus_videoroom_listener_free(janus_videoroom_listener *l) {
g_free(l);
}
-static void janus_videoroom_muxed_listener_free(janus_videoroom_listener_muxed *l) {
- JANUS_LOG(LOG_VERB, "Freeing muxed-listener\n");
- GSList *ls = l->listeners;
- while(ls) {
- janus_videoroom_listener *listener = (janus_videoroom_listener *)ls->data;
- if(listener) {
- janus_videoroom_listener_free(listener);
- }
- ls = ls->next;
- }
- g_slist_free(l->listeners);
- g_free(l);
-}
-
static void janus_videoroom_participant_free(janus_videoroom_participant *p) {
JANUS_LOG(LOG_VERB, "Freeing publisher\n");
g_free(p->display);
diff --git a/sdp-utils.c b/sdp-utils.c
index 1e72307..8c6116b 100644
--- a/sdp-utils.c
+++ b/sdp-utils.c
@@ -38,22 +38,7 @@ void janus_sdp_free(janus_sdp *sdp) {
temp = sdp->m_lines;
while(temp) {
janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
- g_free(m->type_str);
- g_free(m->proto);
- g_free(m->c_addr);
- g_free(m->b_name);
- g_list_free_full(m->fmts, (GDestroyNotify)g_free);
- m->fmts = NULL;
- g_list_free(m->ptypes);
- m->ptypes = NULL;
- GList *temp2 = m->attributes;
- while(temp2) {
- janus_sdp_attribute *a = (janus_sdp_attribute *)temp2->data;
- janus_sdp_attribute_destroy(a);
- temp2 = temp2->next;
- }
- g_list_free(m->attributes);
- g_free(m);
+ janus_sdp_mline_destroy(m);
temp = temp->next;
}
g_list_free(sdp->m_lines);
@@ -61,6 +46,55 @@ void janus_sdp_free(janus_sdp *sdp) {
g_free(sdp);
}
+janus_sdp_mline *janus_sdp_mline_create(janus_sdp_mtype type, guint16 port, const char *proto, janus_sdp_mdirection direction) {
+ janus_sdp_mline *m = g_malloc0(sizeof(janus_sdp_mline));
+ m->type = type;
+ const char *type_str = janus_sdp_mtype_str(type);
+ if(type_str == NULL) {
+ JANUS_LOG(LOG_WARN, "Unknown media type, type_str will have to be set manually\n");
+ } else {
+ m->type_str = g_strdup(type_str);
+ }
+ m->port = port;
+ m->proto = proto ? g_strdup(proto) : NULL;
+ m->direction = direction;
+ return m;
+}
+
+void janus_sdp_mline_destroy(janus_sdp_mline *mline) {
+ if(!mline)
+ return;
+ g_free(mline->type_str);
+ g_free(mline->proto);
+ g_free(mline->c_addr);
+ g_free(mline->b_name);
+ g_list_free_full(mline->fmts, (GDestroyNotify)g_free);
+ mline->fmts = NULL;
+ g_list_free(mline->ptypes);
+ mline->ptypes = NULL;
+ GList *temp = mline->attributes;
+ while(temp) {
+ janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
+ janus_sdp_attribute_destroy(a);
+ temp = temp->next;
+ }
+ g_list_free(mline->attributes);
+ g_free(mline);
+}
+
+janus_sdp_mline *janus_sdp_mline_find(janus_sdp *sdp, janus_sdp_mtype type) {
+ if(sdp == NULL)
+ return NULL;
+ GList *ml = sdp->m_lines;
+ while(ml) {
+ janus_sdp_mline *m = (janus_sdp_mline *)ml->data;
+ if(m->type == type)
+ return m;
+ ml = ml->next;
+ }
+ return NULL;
+}
+
janus_sdp_attribute *janus_sdp_attribute_create(const char *name, const char *value, ...) {
if(!name)
return NULL;
@@ -85,6 +119,72 @@ void janus_sdp_attribute_destroy(janus_sdp_attribute *attr) {
g_free(attr);
}
+int janus_sdp_attribute_add_to_mline(janus_sdp_mline *mline, janus_sdp_attribute *attr) {
+ if(!mline || !attr)
+ return -1;
+ mline->attributes = g_list_append(mline->attributes, attr);
+ return 0;
+}
+
+janus_sdp_mtype janus_sdp_parse_mtype(const char *type) {
+ if(type == NULL)
+ return JANUS_SDP_OTHER;
+ if(!strcasecmp(type, "audio"))
+ return JANUS_SDP_AUDIO;
+ if(!strcasecmp(type, "video"))
+ return JANUS_SDP_VIDEO;
+ if(!strcasecmp(type, "application"))
+ return JANUS_SDP_APPLICATION;
+ return JANUS_SDP_OTHER;
+}
+
+const char *janus_sdp_mtype_str(janus_sdp_mtype type) {
+ switch(type) {
+ case JANUS_SDP_AUDIO:
+ return "audio";
+ case JANUS_SDP_VIDEO:
+ return "video";
+ case JANUS_SDP_APPLICATION:
+ return "application";
+ case JANUS_SDP_OTHER:
+ default:
+ break;
+ }
+ return NULL;
+}
+
+janus_sdp_mdirection janus_sdp_parse_mdirection(const char *direction) {
+ if(direction == NULL)
+ return JANUS_SDP_INVALID;
+ if(!strcasecmp(direction, "sendrecv"))
+ return JANUS_SDP_SENDRECV;
+ if(!strcasecmp(direction, "sendonly"))
+ return JANUS_SDP_SENDONLY;
+ if(!strcasecmp(direction, "recvonly"))
+ return JANUS_SDP_RECVONLY;
+ if(!strcasecmp(direction, "inactive"))
+ return JANUS_SDP_INACTIVE;
+ return JANUS_SDP_INVALID;
+}
+
+const char *janus_sdp_mdirection_str(janus_sdp_mdirection direction) {
+ switch(direction) {
+ case JANUS_SDP_DEFAULT:
+ case JANUS_SDP_SENDRECV:
+ return "sendrecv";
+ case JANUS_SDP_SENDONLY:
+ return "sendonly";
+ case JANUS_SDP_RECVONLY:
+ return "recvonly";
+ case JANUS_SDP_INACTIVE:
+ return "inactive";
+ case JANUS_SDP_INVALID:
+ default:
+ break;
+ }
+ return NULL;
+}
+
janus_sdp *janus_sdp_parse(const char *sdp, char *error, size_t errlen) {
if(!sdp)
return NULL;
@@ -94,6 +194,8 @@ janus_sdp *janus_sdp_parse(const char *sdp, char *error, size_t errlen) {
return NULL;
}
janus_sdp *imported = g_malloc0(sizeof(janus_sdp));
+ imported->o_ipv4 = TRUE;
+ imported->c_ipv4 = TRUE;
gboolean success = TRUE;
janus_sdp_mline *mline = NULL;
@@ -222,17 +324,11 @@ janus_sdp *janus_sdp_parse(const char *sdp, char *error, size_t errlen) {
success = FALSE;
break;
}
- if(!strcasecmp(type, "audio"))
- m->type = JANUS_SDP_AUDIO;
- else if(!strcasecmp(type, "video"))
- m->type = JANUS_SDP_VIDEO;
- else if(!strcasecmp(type, "application"))
- m->type = JANUS_SDP_APPLICATION;
- else
- m->type = JANUS_SDP_OTHER;
+ m->type = janus_sdp_parse_mtype(type);
m->type_str = g_strdup(type);
m->proto = g_strdup(proto);
m->direction = JANUS_SDP_SENDRECV;
+ m->c_ipv4 = TRUE;
if(m->port > 0) {
/* Now let's check the payload types/formats */
gchar **mline_parts = g_strsplit(line+2, " ", -1);
@@ -319,21 +415,10 @@ janus_sdp *janus_sdp_parse(const char *sdp, char *error, size_t errlen) {
char *semicolon = strchr(line, ':');
if(semicolon == NULL) {
/* Is this a media direction attribute? */
- if(!strcasecmp(line, "sendrecv")) {
+ janus_sdp_mdirection direction = janus_sdp_parse_mdirection(line);
+ if(direction != JANUS_SDP_INVALID) {
g_free(a);
- mline->direction = JANUS_SDP_SENDRECV;
- break;
- } else if(!strcasecmp(line, "sendonly")) {
- g_free(a);
- mline->direction = JANUS_SDP_SENDONLY;
- break;
- } else if(!strcasecmp(line, "recvonly")) {
- g_free(a);
- mline->direction = JANUS_SDP_RECVONLY;
- break;
- } else if(!strcasecmp(line, "inactive")) {
- g_free(a);
- mline->direction = JANUS_SDP_INACTIVE;
+ mline->direction = direction;
break;
}
a->name = g_strdup(line);
@@ -408,6 +493,154 @@ int janus_sdp_remove_payload_type(janus_sdp *sdp, int pt) {
return 0;
}
+int janus_sdp_get_codec_pt(janus_sdp *sdp, const char *codec) {
+ if(sdp == NULL || codec == NULL)
+ return -1;
+ /* Check the format string (note that we only parse what browsers can negotiate) */
+ gboolean video = FALSE;
+ const char *format = NULL, *format2 = NULL;
+ if(!strcasecmp(codec, "opus")) {
+ format = "opus/48000/2";
+ format2 = "OPUS/48000/2";
+ } else if(!strcasecmp(codec, "pcmu")) {
+ /* We know the payload type is 0: we just need to make sure it's there */
+ format = "pcmu/8000";
+ format2 = "PCMU/8000";
+ } else if(!strcasecmp(codec, "pcma")) {
+ /* We know the payload type is 8: we just need to make sure it's there */
+ format = "pcma/8000";
+ format2 = "PCMA/8000";
+ } else if(!strcasecmp(codec, "g722")) {
+ /* We know the payload type is 9: we just need to make sure it's there */
+ format = "g722/8000";
+ format2 = "G722/8000";
+ } else if(!strcasecmp(codec, "isac16")) {
+ format = "isac/16000";
+ format2 = "ISAC/16000";
+ } else if(!strcasecmp(codec, "isac32")) {
+ format = "isac/32000";
+ format2 = "ISAC/32000";
+ } else if(!strcasecmp(codec, "dtmf")) {
+ format = "telephone-event/8000";
+ format2 = "TELEPHONE-EVENT/8000";
+ } else if(!strcasecmp(codec, "vp8")) {
+ video = TRUE;
+ format = "vp8/90000";
+ format2 = "VP8/90000";
+ } else if(!strcasecmp(codec, "vp9")) {
+ video = TRUE;
+ format = "vp9/90000";
+ format2 = "VP9/90000";
+ } else if(!strcasecmp(codec, "h264")) {
+ video = TRUE;
+ format = "h264/90000";
+ format2 = "H264/90000";
+ } else {
+ JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", codec);
+ return -1;
+ }
+ /* Check all m->lines */
+ GList *ml = sdp->m_lines;
+ while(ml) {
+ janus_sdp_mline *m = (janus_sdp_mline *)ml->data;
+ if((!video && m->type != JANUS_SDP_AUDIO) || (video && m->type != JANUS_SDP_VIDEO)) {
+ ml = ml->next;
+ continue;
+ }
+ /* Look in all rtpmap attributes */
+ GList *ma = m->attributes;
+ while(ma) {
+ janus_sdp_attribute *a = (janus_sdp_attribute *)ma->data;
+ if(a->name != NULL && a->value != NULL && !strcasecmp(a->name, "rtpmap")) {
+ int pt = atoi(a->value);
+ if(strstr(a->value, format) || strstr(a->value, format2))
+ return pt;
+ }
+ ma = ma->next;
+ }
+ ml = ml->next;
+ }
+ return -1;
+}
+
+const char *janus_sdp_get_codec_name(janus_sdp *sdp, int pt) {
+ if(sdp == NULL || pt < 0)
+ return NULL;
+ if(pt == 0)
+ return "pcmu";
+ if(pt == 8)
+ return "pcma";
+ if(pt == 9)
+ return "g722";
+ GList *ml = sdp->m_lines;
+ while(ml) {
+ janus_sdp_mline *m = (janus_sdp_mline *)ml->data;
+ /* Look in all rtpmap attributes */
+ GList *ma = m->attributes;
+ while(ma) {
+ janus_sdp_attribute *a = (janus_sdp_attribute *)ma->data;
+ if(a->name != NULL && a->value != NULL && !strcasecmp(a->name, "rtpmap")) {
+ int a_pt = atoi(a->value);
+ if(a_pt == pt) {
+ /* Found! */
+ if(strstr(a->value, "vp8") || strstr(a->value, "VP8"))
+ return "vp8";
+ if(strstr(a->value, "vp9") || strstr(a->value, "VP9"))
+ return "vp9";
+ if(strstr(a->value, "h264") || strstr(a->value, "H264"))
+ return "h264";
+ if(strstr(a->value, "opus") || strstr(a->value, "OPUS"))
+ return "opus";
+ if(strstr(a->value, "pcmu") || strstr(a->value, "PMCU"))
+ return "pcmu";
+ if(strstr(a->value, "pcma") || strstr(a->value, "PMCA"))
+ return "pcma";
+ if(strstr(a->value, "g722") || strstr(a->value, "G722"))
+ return "g722";
+ if(strstr(a->value, "isac/16") || strstr(a->value, "ISAC/16"))
+ return "isac16";
+ if(strstr(a->value, "isac/32") || strstr(a->value, "ISAC/32"))
+ return "isac32";
+ if(strstr(a->value, "telephone-event/8000") || strstr(a->value, "telephone-event/8000"))
+ return "dtmf";
+ JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", a->value);
+ return NULL;
+ }
+ }
+ ma = ma->next;
+ }
+ ml = ml->next;
+ }
+ return NULL;
+}
+
+const char *janus_sdp_get_codec_rtpmap(const char *codec) {
+ if(codec == NULL)
+ return NULL;
+ if(!strcasecmp(codec, "opus"))
+ return "opus/48000/2";
+ if(!strcasecmp(codec, "pcmu"))
+ return "PCMU/8000";
+ if(!strcasecmp(codec, "pcma"))
+ return "PCMA/8000";
+ if(!strcasecmp(codec, "g722"))
+ return "G722/8000";
+ if(!strcasecmp(codec, "isac16"))
+ return "ISAC/16000";
+ if(!strcasecmp(codec, "isac32"))
+ return "ISAC/32000";
+ if(!strcasecmp(codec, "dtmf"))
+ return "telephone-event/8000";
+ if(!strcasecmp(codec, "vp8"))
+ return "VP8/90000";
+ if(!strcasecmp(codec, "vp9"))
+ return "VP9/90000";
+ if(!strcasecmp(codec, "h264"))
+ return "H264/90000";
+ JANUS_LOG(LOG_ERR, "Unsupported codec '%s'\n", codec);
+ return NULL;
+}
+
char *janus_sdp_write(janus_sdp *imported) {
if(!imported)
return NULL;
@@ -461,7 +694,7 @@ char *janus_sdp_write(janus_sdp *imported) {
m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(0));
g_strlcat(sdp, " 0", JANUS_BUFSIZE);
} else {
- if(strstr(m->proto, "RTP") != NULL) {
+ if(m->proto != NULL && strstr(m->proto, "RTP") != NULL) {
/* RTP profile, use payload types */
GList *ptypes = m->ptypes;
while(ptypes) {
@@ -493,26 +726,8 @@ char *janus_sdp_write(janus_sdp *imported) {
g_strlcat(sdp, buffer, JANUS_BUFSIZE);
}
}
- /* a= */
- const char *direction = NULL;
- switch(m->direction) {
- case JANUS_SDP_DEFAULT:
- /* Dob't write the direction */
- break;
- case JANUS_SDP_SENDONLY:
- direction = "sendonly";
- break;
- case JANUS_SDP_RECVONLY:
- direction = "recvonly";
- break;
- case JANUS_SDP_INACTIVE:
- direction = "inactive";
- break;
- case JANUS_SDP_SENDRECV:
- default:
- direction = "sendrecv";
- break;
- }
+ /* a= (note that we don't format the direction if it's JANUS_SDP_DEFAULT) */
+ const char *direction = m->direction != JANUS_SDP_DEFAULT ? janus_sdp_mdirection_str(m->direction) : NULL;
if(direction != NULL) {
g_snprintf(buffer, sizeof(buffer), "a=%s\r\n", direction);
g_strlcat(sdp, buffer, JANUS_BUFSIZE);
@@ -542,3 +757,398 @@ char *janus_sdp_write(janus_sdp *imported) {
}
return sdp;
}
+
+janus_sdp *janus_sdp_new(const char *name, const char *address) {
+ janus_sdp *sdp = g_malloc0(sizeof(janus_sdp));
+ /* Fill in some predefined stuff */
+ sdp->version = 0;
+ sdp->o_name = g_strdup("-");
+ sdp->o_sessid = janus_get_real_time();
+ sdp->o_version = 1;
+ sdp->o_ipv4 = TRUE;
+ sdp->o_addr = g_strdup(address ? address : "127.0.0.1");
+ sdp->s_name = g_strdup(name ? name : "Janus session");
+ sdp->t_start = 0;
+ sdp->t_stop = 0;
+ sdp->c_ipv4 = TRUE;
+ sdp->c_addr = g_strdup(address ? address : "127.0.0.1");
+ /* Done */
+ return sdp;
+}
+
+janus_sdp *janus_sdp_generate_offer(const char *name, const char *address, ...) {
+ /* This method has a variable list of arguments, telling us what we should offer */
+ va_list args;
+ va_start(args, address);
+ /* Let's see what we should do with the media */
+ gboolean do_audio = TRUE, do_video = TRUE, do_data = TRUE,
+ audio_dtmf = FALSE, video_rtcpfb = TRUE, h264_fmtp = TRUE;
+ const char *audio_codec = NULL, *video_codec = NULL;
+ int audio_pt = 111, video_pt = 96;
+ janus_sdp_mdirection audio_dir = JANUS_SDP_SENDRECV, video_dir = JANUS_SDP_SENDRECV;
+ int property = va_arg(args, int);
+ while(property != JANUS_SDP_OA_DONE) {
+ if(property == JANUS_SDP_OA_AUDIO) {
+ do_audio = va_arg(args, gboolean);
+ } else if(property == JANUS_SDP_OA_VIDEO) {
+ do_video = va_arg(args, gboolean);
+ } else if(property == JANUS_SDP_OA_DATA) {
+ do_data = va_arg(args, gboolean);
+ } else if(property == JANUS_SDP_OA_AUDIO_DIRECTION) {
+ audio_dir = va_arg(args, janus_sdp_mdirection);
+ } else if(property == JANUS_SDP_OA_VIDEO_DIRECTION) {
+ video_dir = va_arg(args, janus_sdp_mdirection);
+ } else if(property == JANUS_SDP_OA_AUDIO_CODEC) {
+ audio_codec = va_arg(args, char *);
+ } else if(property == JANUS_SDP_OA_VIDEO_CODEC) {
+ video_codec = va_arg(args, char *);
+ } else if(property == JANUS_SDP_OA_AUDIO_PT) {
+ audio_pt = va_arg(args, int);
+ } else if(property == JANUS_SDP_OA_VIDEO_PT) {
+ video_pt = va_arg(args, int);
+ } else if(property == JANUS_SDP_OA_AUDIO_DTMF) {
+ audio_dtmf = va_arg(args, gboolean);
+ } else if(property == JANUS_SDP_OA_VIDEO_RTCPFB_DEFAULTS) {
+ video_rtcpfb = va_arg(args, gboolean);
+ } else if(property == JANUS_SDP_OA_VIDEO_H264_FMTP) {
+ h264_fmtp = va_arg(args, gboolean);
+ } else {
+ JANUS_LOG(LOG_WARN, "Unknown property %d for preparing SDP answer, ignoring...\n", property);
+ }
+ property = va_arg(args, int);
+ }
+ if(audio_codec == NULL)
+ audio_codec = "opus";
+ const char *audio_rtpmap = janus_sdp_get_codec_rtpmap(audio_codec);
+ if(do_audio && audio_rtpmap == NULL) {
+ JANUS_LOG(LOG_ERR, "Unsupported audio codec '%s', can't prepare an offer\n", audio_codec);
+ return NULL;
+ }
+ if(video_codec == NULL)
+ video_codec = "vp8";
+ const char *video_rtpmap = janus_sdp_get_codec_rtpmap(video_codec);
+ if(do_video && video_rtpmap == NULL) {
+ JANUS_LOG(LOG_ERR, "Unsupported video codec '%s', can't prepare an offer\n", video_codec);
+ return NULL;
+ }
+#ifndef HAVE_SCTP
+ do_data = FALSE;
+#endif
+
+ /* Create a new janus_sdp object */
+ janus_sdp *offer = janus_sdp_new(name, address);
+ /* Now add all the media we should */
+ if(do_audio) {
+ janus_sdp_mline *m = janus_sdp_mline_create(JANUS_SDP_AUDIO, 1, "UDP/TLS/RTP/SAVPF", audio_dir);
+ m->c_ipv4 = TRUE;
+ m->c_addr = g_strdup(offer->c_addr);
+ /* Add the selected audio codec */
+ m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(audio_pt));
+ janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", audio_pt, audio_rtpmap);
+ m->attributes = g_list_append(m->attributes, a);
+ /* Check if we need to add a payload type for DTMF tones (telephone-event/8000) */
+ if(audio_dtmf) {
+ /* We do */
+ int dtmf_pt = 126;
+ m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(dtmf_pt));
+ janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", dtmf_pt, janus_sdp_get_codec_rtpmap("dtmf"));
+ m->attributes = g_list_append(m->attributes, a);
+ }
+ offer->m_lines = g_list_append(offer->m_lines, m);
+ }
+ if(do_video) {
+ janus_sdp_mline *m = janus_sdp_mline_create(JANUS_SDP_VIDEO, 1, "UDP/TLS/RTP/SAVPF", video_dir);
+ m->c_ipv4 = TRUE;
+ m->c_addr = g_strdup(offer->c_addr);
+ /* Add the selected video codec */
+ m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(video_pt));
+ janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", video_pt, video_rtpmap);
+ m->attributes = g_list_append(m->attributes, a);
+ if(!strcasecmp(video_codec, "h264") && h264_fmtp) {
+ /* If it's H.264 and we were asked to, add the default fmtp profile as well */
+ a = janus_sdp_attribute_create("fmtp", "%d profile-level-id=42e01f;packetization-mode=1", video_pt);
+ m->attributes = g_list_append(m->attributes, a);
+ }
+ if(video_rtcpfb) {
+ /* Add rtcp-fb attributes */
+ a = janus_sdp_attribute_create("rtcp-fb", "%d ccm fir", video_pt);
+ m->attributes = g_list_append(m->attributes, a);
+ a = janus_sdp_attribute_create("rtcp-fb", "%d nack", video_pt);
+ m->attributes = g_list_append(m->attributes, a);
+ a = janus_sdp_attribute_create("rtcp-fb", "%d nack pli", video_pt);
+ m->attributes = g_list_append(m->attributes, a);
+ a = janus_sdp_attribute_create("rtcp-fb", "%d goog-remb", video_pt);
+ m->attributes = g_list_append(m->attributes, a);
+ }
+ offer->m_lines = g_list_append(offer->m_lines, m);
+ }
+ if(do_data) {
+ janus_sdp_mline *m = janus_sdp_mline_create(JANUS_SDP_APPLICATION, 1, "DTLS/SCTP", JANUS_SDP_DEFAULT);
+ m->c_ipv4 = TRUE;
+ m->c_addr = g_strdup(offer->c_addr);
+ m->fmts = g_list_append(m->fmts, g_strdup("5000"));
+ /* Add an sctmap attribute */
+ janus_sdp_attribute *aa = janus_sdp_attribute_create("sctmap", "5000 webrtc-datachannel 16");
+ m->attributes = g_list_append(m->attributes, aa);
+ offer->m_lines = g_list_append(offer->m_lines, m);
+ }
+
+ /* Done */
+ va_end(args);
+
+ return offer;
+}
+
+janus_sdp *janus_sdp_generate_answer(janus_sdp *offer, ...) {
+ if(offer == NULL)
+ return NULL;
+
+ /* This method has a variable list of arguments, telling us how we should respond */
+ va_list args;
+ va_start(args, offer);
+ /* Let's see what we should do with the media */
+ gboolean do_audio = TRUE, do_video = TRUE, do_data = TRUE,
+ audio_dtmf = FALSE, video_rtcpfb = TRUE, h264_fmtp = TRUE;
+ const char *audio_codec = NULL, *video_codec = NULL;
+ janus_sdp_mdirection audio_dir = JANUS_SDP_SENDRECV, video_dir = JANUS_SDP_SENDRECV;
+ int property = va_arg(args, int);
+ while(property != JANUS_SDP_OA_DONE) {
+ if(property == JANUS_SDP_OA_AUDIO) {
+ do_audio = va_arg(args, gboolean);
+ } else if(property == JANUS_SDP_OA_VIDEO) {
+ do_video = va_arg(args, gboolean);
+ } else if(property == JANUS_SDP_OA_DATA) {
+ do_data = va_arg(args, gboolean);
+ } else if(property == JANUS_SDP_OA_AUDIO_DIRECTION) {
+ audio_dir = va_arg(args, janus_sdp_mdirection);
+ } else if(property == JANUS_SDP_OA_VIDEO_DIRECTION) {
+ video_dir = va_arg(args, janus_sdp_mdirection);
+ } else if(property == JANUS_SDP_OA_AUDIO_CODEC) {
+ audio_codec = va_arg(args, char *);
+ } else if(property == JANUS_SDP_OA_VIDEO_CODEC) {
+ video_codec = va_arg(args, char *);
+ } else if(property == JANUS_SDP_OA_AUDIO_DTMF) {
+ audio_dtmf = va_arg(args, gboolean);
+ } else if(property == JANUS_SDP_OA_VIDEO_RTCPFB_DEFAULTS) {
+ video_rtcpfb = va_arg(args, gboolean);
+ } else if(property == JANUS_SDP_OA_VIDEO_H264_FMTP) {
+ h264_fmtp = va_arg(args, gboolean);
+ } else {
+ JANUS_LOG(LOG_WARN, "Unknown property %d for preparing SDP answer, ignoring...\n", property);
+ }
+ property = va_arg(args, int);
+ }
+#ifndef HAVE_SCTP
+ do_data = FALSE;
+#endif
+
+ janus_sdp *answer = g_malloc0(sizeof(janus_sdp));
+ /* Start by copying some of the headers */
+ answer->version = offer->version;
+ answer->o_name = g_strdup(offer->o_name ? offer->o_name : "-");
+ answer->o_sessid = offer->o_sessid;
+ answer->o_version = offer->o_version;
+ answer->o_ipv4 = offer->o_ipv4;
+ answer->o_addr = g_strdup(offer->o_addr ? offer->o_addr : "127.0.0.1");
+ answer->s_name = g_strdup(offer->s_name ? offer->s_name : "Janus session");
+ answer->t_start = 0;
+ answer->t_stop = 0;
+ answer->c_ipv4 = offer->c_ipv4;
+ answer->c_addr = g_strdup(offer->c_addr ? offer->c_addr : "127.0.0.1");
+
+ /* Now iterate on all media, and let's see what we should do */
+ int audio = 0, video = 0, data = 0;
+ GList *temp = offer->m_lines;
+ while(temp) {
+ janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
+ /* For each m-line we parse, we'll need a corresponding one in the answer */
+ janus_sdp_mline *am = g_malloc0(sizeof(janus_sdp_mline));
+ am->type = m->type;
+ am->type_str = m->type_str ? g_strdup(m->type_str) : NULL;
+ am->proto = g_strdup(m->proto ? m->proto : "UDP/TLS/RTP/SAVPF");
+ am->port = m->port;
+ am->c_ipv4 = m->c_ipv4;
+ am->c_addr = g_strdup(am->c_addr ? am->c_addr : "127.0.0.1");
+ am->direction = JANUS_SDP_INACTIVE; /* We'll change this later */
+ /* Append to the list of m-lines in the answer */
+ answer->m_lines = g_list_append(answer->m_lines, am);
+ /* Let's see what this is */
+ if(m->type == JANUS_SDP_AUDIO) {
+ if(m->port > 0) {
+ audio++;
+ }
+ if(!do_audio || audio > 1) {
+ /* Reject */
+ am->port = 0;
+ temp = temp->next;
+ continue;
+ }
+ } else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
+ if(m->port > 0) {
+ video++;
+ }
+ if(!do_video || video > 1) {
+ /* Reject */
+ am->port = 0;
+ temp = temp->next;
+ continue;
+ }
+ } else if(m->type == JANUS_SDP_APPLICATION && m->port > 0) {
+ if(m->port > 0) {
+ data++;
+ }
+ if(!do_data || data > 1) {
+ /* Reject */
+ am->port = 0;
+ temp = temp->next;
+ continue;
+ }
+ }
+ if(m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO) {
+ janus_sdp_mdirection target_dir = m->type == JANUS_SDP_AUDIO ? audio_dir : video_dir;
+ /* What is the direction we were offered? And how were we asked to react?
+ * Adapt the direction in our answer accordingly */
+ switch(m->direction) {
+ case JANUS_SDP_RECVONLY:
+ if(target_dir == JANUS_SDP_SENDRECV || target_dir == JANUS_SDP_SENDONLY) {
+ /* Peer is recvonly, we'll only send */
+ am->direction = JANUS_SDP_SENDONLY;
+ } else {
+ /* Peer is recvonly, but we're not ok to send, so reply with inactive */
+ JANUS_LOG(LOG_WARN, "%s offered as '%s', but we need '%s': using 'inactive'\n",
+ m->type == JANUS_SDP_AUDIO ? "Audio" : "Video",
+ janus_sdp_mdirection_str(m->direction), janus_sdp_mdirection_str(target_dir));
+ am->direction = JANUS_SDP_INACTIVE;
+ }
+ break;
+ case JANUS_SDP_SENDONLY:
+ if(target_dir == JANUS_SDP_SENDRECV || target_dir == JANUS_SDP_RECVONLY) {
+ /* Peer is sendonly, we'll only receive */
+ am->direction = JANUS_SDP_RECVONLY;
+ } else {
+ /* Peer is sendonly, but we're not ok to receive, so reply with inactive */
+ JANUS_LOG(LOG_WARN, "%s offered as '%s', but we need '%s': using 'inactive'\n",
+ m->type == JANUS_SDP_AUDIO ? "Audio" : "Video",
+ janus_sdp_mdirection_str(m->direction), janus_sdp_mdirection_str(target_dir));
+ am->direction = JANUS_SDP_INACTIVE;
+ }
+ break;
+ case JANUS_SDP_INACTIVE:
+ /* Peer inactive, set inactive in the answer to */
+ am->direction = JANUS_SDP_INACTIVE;
+ break;
+ case JANUS_SDP_SENDRECV:
+ default:
+ /* The peer is fine with everything, so use our constraint */
+ am->direction = target_dir;
+ break;
+ }
+ /* Look for the right codec and stick to that */
+ const char *codec = m->type == JANUS_SDP_AUDIO ? audio_codec : video_codec;
+ if(codec == NULL) {
+ /* FIXME User didn't provide a codec to accept? Let's see if Opus (for audio)
+ * of VP8 (for video) were negotiated: if so, use them, otherwise let's
+ * pick some other codec we know about among the ones that were offered.
+ * Notice that if it's not a codec we understand, we reject the medium,
+ * as browsers would reject it anyway. If you need more flexibility you'll
+ * have to generate an answer yourself, rather than automatically... */
+ codec = m->type == JANUS_SDP_AUDIO ? "opus" : "vp8";
+ if(janus_sdp_get_codec_pt(offer, codec) < 0) {
+ /* We couldn't find our preferred codec, let's try something else */
+ if(m->type == JANUS_SDP_AUDIO) {
+ /* Opus not found, maybe mu-law? */
+ codec = "pcmu";
+ if(janus_sdp_get_codec_pt(offer, codec) < 0) {
+ /* mu-law not found, maybe a-law? */
+ codec = "pcma";
+ if(janus_sdp_get_codec_pt(offer, codec) < 0) {
+ /* a-law not found, maybe G.722? */
+ codec = "722";
+ if(janus_sdp_get_codec_pt(offer, codec) < 0) {
+ /* G.722 not found, maybe isac32? */
+ codec = "isac32";
+ if(janus_sdp_get_codec_pt(offer, codec) < 0) {
+ /* isac32 not found, maybe isac16? */
+ codec = "isac16";
+ }
+ }
+ }
+ }
+ } else {
+ /* VP8 not found, maybe VP9? */
+ codec = "vp9";
+ if(janus_sdp_get_codec_pt(offer, codec) < 0) {
+ /* VP9 not found either, maybe H.264? */
+ codec = "h264";
+ }
+ }
+ }
+ }
+ int pt = janus_sdp_get_codec_pt(offer, codec);
+ if(pt < 0) {
+ /* Reject */
+ JANUS_LOG(LOG_WARN, "Couldn't find codec we needed (%s) in the offer, rejecting %s\n",
+ codec, m->type == JANUS_SDP_AUDIO ? "audio" : "video");
+ am->port = 0;
+ am->direction = JANUS_SDP_INACTIVE;
+ temp = temp->next;
+ continue;
+ }
+ am->ptypes = g_list_append(am->ptypes, GINT_TO_POINTER(pt));
+ /* Add the related attributes */
+ if(m->type == JANUS_SDP_AUDIO) {
+ /* Add rtpmap attribute */
+ janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", pt, janus_sdp_get_codec_rtpmap(codec));
+ am->attributes = g_list_append(am->attributes, a);
+ /* Check if we need to add a payload type for DTMF tones (telephone-event/8000) */
+ if(audio_dtmf) {
+ int dtmf_pt = janus_sdp_get_codec_pt(offer, "dtmf");
+ if(dtmf_pt >= 0) {
+ /* We do */
+ am->ptypes = g_list_append(am->ptypes, GINT_TO_POINTER(dtmf_pt));
+ janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", dtmf_pt, janus_sdp_get_codec_rtpmap("dtmf"));
+ am->attributes = g_list_append(am->attributes, a);
+ }
+ }
+ } else {
+ /* Add rtpmap attribute */
+ janus_sdp_attribute *a = janus_sdp_attribute_create("rtpmap", "%d %s", pt, janus_sdp_get_codec_rtpmap(codec));
+ am->attributes = g_list_append(am->attributes, a);
+ if(!strcasecmp(codec, "h264") && h264_fmtp) {
+ /* If it's H.264 and we were asked to, add the default fmtp profile as well */
+ a = janus_sdp_attribute_create("fmtp", "%d profile-level-id=42e01f;packetization-mode=1", pt);
+ am->attributes = g_list_append(am->attributes, a);
+ }
+ if(video_rtcpfb) {
+ /* Add rtcp-fb attributes */
+ a = janus_sdp_attribute_create("rtcp-fb", "%d ccm fir", pt);
+ am->attributes = g_list_append(am->attributes, a);
+ a = janus_sdp_attribute_create("rtcp-fb", "%d nack", pt);
+ am->attributes = g_list_append(am->attributes, a);
+ a = janus_sdp_attribute_create("rtcp-fb", "%d nack pli", pt);
+ am->attributes = g_list_append(am->attributes, a);
+ a = janus_sdp_attribute_create("rtcp-fb", "%d goog-remb", pt);
+ am->attributes = g_list_append(am->attributes, a);
+ }
+ }
+ } else {
+ /* This is for data, add formats and an sctmap attribute */
+ am->direction = JANUS_SDP_DEFAULT;
+ GList *fmt = m->fmts;
+ while(fmt) {
+ char *fmt_str = (char *)fmt->data;
+ if(fmt_str)
+ am->fmts = g_list_append(am->fmts, g_strdup(fmt_str));
+ fmt = fmt->next;
+ }
+ janus_sdp_attribute *aa = janus_sdp_attribute_create("sctmap", "5000 webrtc-datachannel 16");
+ am->attributes = g_list_append(am->attributes, aa);
+ }
+ temp = temp->next;
+ }
+
+ /* Done */
+ va_end(args);
+
+ return answer;
+}
diff --git a/sdp-utils.h b/sdp-utils.h
index 2c11f96..faf4aac 100644
--- a/sdp-utils.h
+++ b/sdp-utils.h
@@ -60,6 +60,14 @@ typedef enum janus_sdp_mtype {
/*! \brief m=whatever (we don't care, unsupported) */
JANUS_SDP_OTHER
} janus_sdp_mtype;
+/*! \brief Helper method to get a janus_sdp_mtype from a string
+ * @param[in] type The type to parse as a string (e.g., "audio")
+ * @returns The corresponding janus_sdp_mtype value */
+janus_sdp_mtype janus_sdp_parse_mtype(const char *type);
+/*! \brief Helper method to get the string associated to a janus_sdp_mtype value
+ * @param[in] type The type to stringify
+ * @returns The type as a string, if valid, or NULL otherwise */
+const char *janus_sdp_mtype_str(janus_sdp_mtype type);
/*! \brief Helper enumeration to quickly identify m-line directions */
typedef enum janus_sdp_mdirection {
@@ -72,8 +80,18 @@ typedef enum janus_sdp_mdirection {
/*! \brief recvonly */
JANUS_SDP_RECVONLY,
/*! \brief inactive */
- JANUS_SDP_INACTIVE
+ JANUS_SDP_INACTIVE,
+ /*! \brief invalid direction (when parsing) */
+ JANUS_SDP_INVALID
} janus_sdp_mdirection;
+/*! \brief Helper method to get a janus_sdp_mdirection from a string
+ * @param[in] direction The direction to parse as a string (e.g., "sendrecv")
+ * @returns The corresponding janus_sdp_mdirection value */
+janus_sdp_mdirection janus_sdp_parse_mdirection(const char *direction);
+/*! \brief Helper method to get the string associated to a janus_sdp_mdirection value
+ * @param[in] direction The direction to stringify
+ * @returns The direction as a string, if valid, or NULL otherwise */
+const char *janus_sdp_mdirection_str(janus_sdp_mdirection direction);
/*! \brief SDP m-line representation */
typedef struct janus_sdp_mline {
@@ -102,6 +120,28 @@ typedef struct janus_sdp_mline {
/*! \brief List of m-line attributes */
GList *attributes;
} janus_sdp_mline;
+/*! \brief Helper method to quickly create a janus_sdp_mline instance
+ * @note The \c type_str property of the new m-line is created automatically
+ * depending on the provided \c type attribute. If \c type is JANUS_SDP_OTHER,
+ * though, \c type_str will NOT we allocated, and will be up to the caller.
+ * @param[in] type Type of the media (audio/video/application) as a janus_sdp_mtype
+ * @param[in] port Port to advertise
+ * @param[in] proto Profile to advertise
+ * @param[in] type Direction of the media as a janus_sdp_direction
+ * @returns A pointer to a valid janus_sdp_mline instance, if successfull, NULL otherwise */
+janus_sdp_mline *janus_sdp_mline_create(janus_sdp_mtype type, guint16 port, const char *proto, janus_sdp_mdirection direction);
+/*! \brief Helper method to free a janus_sdp_mline instance
+ * @note This method does not remove the m-line from the janus_sdp instance, that's up to the caller
+ * @param[in] mline The janus_sdp_mline instance to free */
+void janus_sdp_mline_destroy(janus_sdp_mline *mline);
+/*! \brief Helper method to get the janus_sdp_mline associated to a media type
+ * @note This currently returns the first m-line of the specified type it finds: in
+ * general, it shouldn't be an issue as we currently only support a single stream
+ * of the same type per session anyway... this will need to be fixed in the future.
+ * @param[in] sdp The Janus SDP object to search
+ * @param[in] type The type of media to search
+ * @returns The janus_sdp_mline instance, if found, or NULL otherwise */
+janus_sdp_mline *janus_sdp_mline_find(janus_sdp *sdp, janus_sdp_mtype type);
/*! \brief SDP a= attribute representation */
typedef struct janus_sdp_attribute {
@@ -119,6 +159,11 @@ janus_sdp_attribute *janus_sdp_attribute_create(const char *name, const char *va
* @note This method does not remove the attribute from the global or m-line attributes, that's up to the caller
* @param[in] attr The janus_sdp_attribute instance to free */
void janus_sdp_attribute_destroy(janus_sdp_attribute *attr);
+/*! \brief Helper method to add an attribute to a media line
+ * @param[in] mline The m-line to add the attribute to
+ * @param[in] attr The attribute to add
+ * @returns 0 in case of success, -1 otherwise */
+int janus_sdp_attribute_add_to_mline(janus_sdp_mline *mline, janus_sdp_attribute *attr);
/*! \brief Method to parse an SDP string to a janus_sdp object
* @param[in] sdp The SDP string to parse
@@ -139,8 +184,102 @@ int janus_sdp_remove_payload_type(janus_sdp *sdp, int pt);
* @returns A pointer to a string with the serialized SDP, if successful, NULL otherwise */
char *janus_sdp_write(janus_sdp *sdp);
+/*! \brief Method to quickly generate a janus_sdp instance from a few selected fields
+ * @note This allocates the \c o_addr, \c s_name and \c c_addr properties: if you
+ * want to replace them, don't remember to \c g_free the original pointers first.
+ * @param[in] name The session name (if NULL, a default value will be set)
+ * @param[in] address The IP to set in o= and c= fields (if NULL, a default value will be set)
+ * @returns A pointer to a janus_sdp object, if successful, NULL otherwise */
+janus_sdp *janus_sdp_new(const char *name, const char *address);
+
/*! \brief Method to free a Janus SDP object
* @param[in] sdp The Janus SDP object to free */
void janus_sdp_free(janus_sdp *sdp);
+/*! \brief When generating an offer or answer automatically, accept/reject audio if offered (depends on value that follows) */
+#define JANUS_SDP_OA_AUDIO 1
+/*! \brief When generating an offer or answer automatically, accept/reject video if offered (depends on value that follows) */
+#define JANUS_SDP_OA_VIDEO 2
+/*! \brief When generating an offer or answer automatically, accept/reject datachannels if offered (depends on value that follows) */
+#define JANUS_SDP_OA_DATA 3
+/*! \brief When generating an offer or answer automatically, use this direction for audio (depends on value that follows) */
+#define JANUS_SDP_OA_AUDIO_DIRECTION 4
+/*! \brief When generating an offer or answer automatically, use this direction for video (depends on value that follows) */
+#define JANUS_SDP_OA_VIDEO_DIRECTION 5
+/*! \brief When generating an offer or answer automatically, use this codec for audio (depends on value that follows) */
+#define JANUS_SDP_OA_AUDIO_CODEC 6
+/*! \brief When generating an offer or answer automatically, use this codec for video (depends on value that follows) */
+#define JANUS_SDP_OA_VIDEO_CODEC 7
+/*! \brief When generating an offer (this is ignored for answers), use this payload type for audio (depends on value that follows) */
+#define JANUS_SDP_OA_AUDIO_PT 8
+/*! \brief When generating an offer (this is ignored for answers), use this payload type for video (depends on value that follows) */
+#define JANUS_SDP_OA_VIDEO_PT 9
+/*! \brief When generating an offer or answer automatically, do or do not negotiate telephone events (FIXME telephone-event/8000 only) */
+#define JANUS_SDP_OA_AUDIO_DTMF 10
+/*! \brief When generating an offer or answer automatically, do or do not add the rtcpfb attributes we typically negotiate (fir, nack, pli, remb) */
+#define JANUS_SDP_OA_VIDEO_RTCPFB_DEFAULTS 11
+/*! \brief When generating an offer or answer automatically, do or do not add the default fmtp attribute for H.264 (profile-level-id=42e01f;packetization-mode=1) */
+#define JANUS_SDP_OA_VIDEO_H264_FMTP 12
+/*! \brief MUST be used as the last argument in janus_sdp_generate_answer */
+#define JANUS_SDP_OA_DONE 0
+
+/*! \brief Method to generate a janus_sdp offer, using variable arguments to dictate
+ * what to negotiate (e.g., in terms of media to offer, directions, etc.). Variable
+ * arguments are in the form of a sequence of name-value terminated by a JANUS_SDP_OA_DONE, e.g.:
+ \verbatim
+ janus_sdp *offer = janus_sdp_generate_offer("My session", "127.0.0.1",
+ JANUS_SDP_OA_AUDIO, TRUE,
+ JANUS_SDP_OA_AUDIO_PT, 100,
+ JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_SENDONLY,
+ JANUS_SDP_OA_AUDIO_CODEC, "opus",
+ JANUS_SDP_OA_VIDEO, FALSE,
+ JANUS_SDP_OA_DATA, FALSE,
+ JANUS_SDP_OA_DONE);
+ \endverbatim
+ * to only offer a \c sendonly Opus audio stream being offered with 100 as
+ * payload type, and avoid video and datachannels. Refer to the property names in
+ * the header file for a complete list of how you can drive the offer.
+ * The default, if not specified, is to offer everything, using Opus with pt=111
+ * for audio, VP8 with pt=96 as video, and data channels, all as \c sendrecv.
+ * @param[in] name The session name (if NULL, a default value will be set)
+ * @param[in] address The IP to set in o= and c= fields (if NULL, a default value will be set)
+ * @returns A pointer to a janus_sdp object, if successful, NULL otherwise */
+janus_sdp *janus_sdp_generate_offer(const char *name, const char *address, ...);
+/*! \brief Method to generate a janus_sdp answer to a provided janus_sdp offer, using variable arguments
+ * to dictate how to responde (e.g., in terms of media to accept, reject, directions, etc.). Variable
+ * arguments are in the form of a sequence of name-value terminated by a JANUS_SDP_OA_DONE, e.g.:
+ \verbatim
+ janus_sdp *answer = janus_sdp_generate_answer(offer,
+ JANUS_SDP_OA_AUDIO, TRUE,
+ JANUS_SDP_OA_AUDIO_DIRECTION, JANUS_SDP_RECVONLY,
+ JANUS_SDP_OA_AUDIO_CODEC, "opus",
+ JANUS_SDP_OA_VIDEO, FALSE,
+ JANUS_SDP_OA_DATA, FALSE,
+ JANUS_SDP_OA_DONE);
+ \endverbatim
+ * to only accept the audio stream being offered, but as \c recvonly, use Opus
+ * and reject both video and datachannels. Refer to the property names in
+ * the header file for a complete list of how you can drive the answer.
+ * The default, if not specified, is to accept everything as \c sendrecv.
+ * @param[in] offer The Janus SDP offer to respond to
+ * @returns A pointer to a janus_sdp object, if successful, NULL otherwise */
+janus_sdp *janus_sdp_generate_answer(janus_sdp *offer, ...);
+
+/*! \brief Helper to get the payload type associated to a specific codec
+ * @param sdp The Janus SDP instance to process
+ * @param codec The codec to find, as a string
+ * @returns The payload type, if found, or -1 otherwise */
+int janus_sdp_get_codec_pt(janus_sdp *sdp, const char *codec);
+
+/*! \brief Helper to get the codec name associated to a specific payload type
+ * @param sdp The Janus SDP instance to process
+ * @param pt The payload type to find
+ * @returns The codec name, if found, or NULL otherwise */
+const char *janus_sdp_get_codec_name(janus_sdp *sdp, int pt);
+
+/*! \brief Helper to get the rtpmap associated to a specific codec
+ * @param codec The codec name, as a string (e.g., "opus")
+ * @returns The rtpmap value, if found (e.g., "opus/48000/2"), or -1 otherwise */
+const char *janus_sdp_get_codec_rtpmap(const char *codec);
+
#endif
diff --git a/utils.c b/utils.c
index 5d9fd05..10a69be 100644
--- a/utils.c
+++ b/utils.c
@@ -246,6 +246,11 @@ int janus_get_codec_pt(const char *sdp, const char *codec) {
video = 0;
format = "pcma/8000";
format2 = "PCMA/8000";
+ } else if(!strcasecmp(codec, "g722")) {
+ /* We know the payload type is 9: we just need to make sure it's there */
+ video = 0;
+ format = "g722/8000";
+ format2 = "G722/8000";
} else if(!strcasecmp(codec, "isac16")) {
video = 0;
format = "isac/16000";
@@ -318,6 +323,8 @@ const char *janus_get_codec_from_pt(const char *sdp, int pt) {
return "pcmu";
if(pt == 8)
return "pcma";
+ if(pt == 9)
+ return "g722";
/* Look for the mapping */
char rtpmap[50];
g_snprintf(rtpmap, 50, "a=rtpmap:%d ", pt);
@@ -343,6 +350,8 @@ const char *janus_get_codec_from_pt(const char *sdp, int pt) {
return "pcmu";
if(strstr(name, "pcma") || strstr(name, "PMCA"))
return "pcma";
+ if(strstr(name, "g722") || strstr(name, "G722"))
+ return "g722";
if(strstr(name, "isac/16") || strstr(name, "ISAC/16"))
return "isac16";
if(strstr(name, "isac/32") || strstr(name, "ISAC/32"))
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/janus.git
More information about the Pkg-voip-commits
mailing list