static int priv_set_remote_sdp(SscMedia *self, const gchar *str) { su_home_t *home = self->sm_home; const char *pa_error; int res = 0, dlen = strlen(str); g_debug(__func__); if (self->sm_sdp_remote) sdp_parser_free(self->sm_sdp_remote); /* XXX: only update if SDP has really changed */ /* g_message("parsing SDP:\n%s\n---", str); */ self->sm_sdp_remote = sdp_parse(home, str, dlen, sdp_f_insane); pa_error = sdp_parsing_error(self->sm_sdp_remote); if (pa_error) { g_warning("%s: error parsing SDP: %s\n", __func__, pa_error); res = -1; } else { if (self->sm_sdp_remote_str) g_free(self->sm_sdp_remote_str); } return res; }
static void mrcp_sofia_on_session_ready( int status, mrcp_sofia_agent_t *sofia_agent, nua_handle_t *nh, mrcp_sofia_session_t *sofia_session, sip_t const *sip, tagi_t tags[]) { const char *local_sdp_str = NULL, *remote_sdp_str = NULL; mrcp_session_descriptor_t *descriptor = NULL; tl_gets(tags, SOATAG_LOCAL_SDP_STR_REF(local_sdp_str), SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str), TAG_END()); if(remote_sdp_str) { sdp_parser_t *parser = NULL; sdp_session_t *sdp = NULL; apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP\n%s", remote_sdp_str); parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0); sdp = sdp_session(parser); descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,sofia_session->session->pool); sdp_parser_free(parser); } mrcp_session_answer(sofia_session->session,descriptor); }
static void mrcp_sofia_on_resource_discover( int status, mrcp_sofia_agent_t *sofia_agent, nua_handle_t *nh, mrcp_sofia_session_t *sofia_session, sip_t const *sip, tagi_t tags[]) { mrcp_session_t *session = sofia_session->session; if(session) { const char *remote_sdp_str = NULL; mrcp_session_descriptor_t *descriptor = NULL; if(sip->sip_payload) { remote_sdp_str = sip->sip_payload->pl_data; } if(remote_sdp_str) { sdp_parser_t *parser = NULL; sdp_session_t *sdp = NULL; apt_obj_log(APT_LOG_MARK,APT_PRIO_INFO,session->obj,"Resource Discovery SDP %s\n%s", session->name, remote_sdp_str); parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0); sdp = sdp_session(parser); descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,NULL,session->pool); descriptor->response_code = status; sdp_parser_free(parser); } mrcp_session_discover_response(session,descriptor); } }
/* Pre-parse SDP: is this SDP valid? how many audio/video lines? */ janus_sdp *janus_sdp_preparse(const char *jsep_sdp, int *audio, int *video) { sdp_parser_t *parser = sdp_parse(home, jsep_sdp, strlen(jsep_sdp), 0); sdp_session_t *parsed_sdp = sdp_session(parser); if(!parsed_sdp) { JANUS_DEBUG(" Error parsing SDP? %s\n", sdp_parsing_error(parser)); sdp_parser_free(parser); /* Invalid SDP */ return NULL; } sdp_media_t *m = parsed_sdp->sdp_media; while(m) { if(m->m_type == sdp_media_audio) { *audio = *audio + 1; } else if(m->m_type == sdp_media_video) { *video = *video + 1; } m = m->m_next; } janus_sdp *sdp = (janus_sdp *)calloc(1, sizeof(janus_sdp)); if(sdp == NULL) { JANUS_DEBUG("Memory error!\n"); return NULL; } sdp->parser = parser; sdp->sdp = parsed_sdp; return sdp; }
static void mrcp_sofia_on_resource_discover( int status, mrcp_sofia_agent_t *sofia_agent, nua_handle_t *nh, mrcp_sofia_session_t *sofia_session, sip_t const *sip, tagi_t tags[]) { mrcp_session_t *session = sofia_session->session; if(session) { const char *remote_sdp_str = NULL; mrcp_session_descriptor_t *descriptor = NULL; tl_gets(tags, SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str), TAG_END()); if(remote_sdp_str) { sdp_parser_t *parser = NULL; sdp_session_t *sdp = NULL; apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Resource Discovery SDP %s\n%s", session->name, remote_sdp_str); parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0); sdp = sdp_session(parser); descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,NULL,session->pool); sdp_parser_free(parser); } mrcp_session_discover_response(session,descriptor); } }
/* * sipsdp_create_from_buf() * * Parse buffer into common SDP structure * * Returns: NULL if non-recoverable error encountered * */ cc_sdp_t * sipsdp_create_from_buf (char *buf, uint32_t nbytes, cc_sdp_t *sdp) { const char *fname = "sipsdp_create_from_buf"; cc_sdp_t *sip_info = NULL; if (!buf) { return (NULL); } if (sdp) { sip_info = sdp; } else { sipsdp_src_dest_create(CCSIP_DEST_SDP_BIT | CCSIP_SRC_SDP_BIT, &sip_info); } if (!sip_info) { /* * make sure that the src_sdp and dest_sdp are * valid as well. */ return (NULL); } if (sdp_parse(sip_info->dest_sdp, &buf, (uint16_t)nbytes) != SDP_SUCCESS) { sipsdp_src_dest_free(CCSIP_DEST_SDP_BIT | CCSIP_SRC_SDP_BIT, &sip_info); CCSIP_DEBUG_ERROR(SIP_F_PREFIX"Error parsing SDP\n", fname); return (NULL); } return (sip_info); }
/** Generate MRCP descriptor by RTSP request */ MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_rtsp_request( const rtsp_message_t *request, const char *force_destination_ip, const apr_table_t *resource_map, apr_pool_t *pool, su_home_t *home) { mrcp_session_descriptor_t *descriptor = NULL; const char *resource_name = mrcp_name_get_by_rtsp_name( resource_map, request->start_line.common.request_line.resource_name); if(!resource_name) { return NULL; } if(request->start_line.common.request_line.method_id == RTSP_METHOD_SETUP) { if(rtsp_header_property_check(&request->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE) == TRUE && rtsp_header_property_check(&request->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH) == TRUE && request->body.buf) { sdp_parser_t *parser; sdp_session_t *sdp; parser = sdp_parse(home,request->body.buf,request->body.length,0); sdp = sdp_session(parser); if(sdp) { descriptor = mrcp_session_descriptor_create(pool); mrcp_descriptor_generate_by_sdp_session(descriptor,sdp,force_destination_ip,pool); } else { apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse SDP Message"); } sdp_parser_free(parser); } else { /* create default descriptor in case RTSP SETUP contains no SDP */ mpf_rtp_media_descriptor_t *media; descriptor = mrcp_session_descriptor_create(pool); media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t)); mpf_rtp_media_descriptor_init(media); media->state = MPF_MEDIA_ENABLED; media->id = mrcp_session_audio_media_add(descriptor,media); if(rtsp_header_property_check(&request->header.property_set,RTSP_HEADER_FIELD_TRANSPORT) == TRUE) { media->port = request->header.transport.client_port_range.min; media->ip = request->header.transport.destination; } } if(descriptor) { apt_string_assign(&descriptor->resource_name,resource_name,pool); descriptor->resource_state = TRUE; } } else if(request->start_line.common.request_line.method_id == RTSP_METHOD_TEARDOWN) { descriptor = mrcp_session_descriptor_create(pool); apt_string_assign(&descriptor->resource_name,resource_name,pool); descriptor->resource_state = FALSE; } return descriptor; }
static int sdp_read_header(AVFormatContext *s, AVFormatParameters *ap) { RTSPState *rt = s->priv_data; RTSPStream *rtsp_st; int size, i, err; char *content; char url[1024]; AVStream *st; /* read the whole sdp file */ /* XXX: better loading */ content = av_malloc(SDP_MAX_SIZE); size = get_buffer(s->pb, content, SDP_MAX_SIZE - 1); if (size <= 0) { av_free(content); return AVERROR_INVALIDDATA; } content[size] ='\0'; sdp_parse(s, content); av_free(content); /* open each RTP stream */ for(i=0;i<rt->nb_rtsp_streams;i++) { rtsp_st = rt->rtsp_streams[i]; snprintf(url, sizeof(url), "rtp://%s:%d?localport=%d&ttl=%d", inet_ntoa(rtsp_st->sdp_ip), rtsp_st->sdp_port, rtsp_st->sdp_port, rtsp_st->sdp_ttl); if (url_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) { err = AVERROR_INVALIDDATA; goto fail; } /* open the RTP context */ st = NULL; if (rtsp_st->stream_index >= 0) st = s->streams[rtsp_st->stream_index]; if (!st) s->ctx_flags |= AVFMTCTX_NOHEADER; rtsp_st->rtp_ctx = rtp_parse_open(s, st, rtsp_st->rtp_handle, rtsp_st->sdp_payload_type, &rtsp_st->rtp_payload_data); if (!rtsp_st->rtp_ctx) { err = AVERROR(ENOMEM); goto fail; } else { if(rtsp_st->dynamic_handler) { rtsp_st->rtp_ctx->dynamic_protocol_context= rtsp_st->dynamic_protocol_context; rtsp_st->rtp_ctx->parse_packet= rtsp_st->dynamic_handler->parse_packet; } } } return 0; fail: rtsp_close_streams(rt); return err; }
/** Generate MRCP descriptor by RTSP response */ MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_rtsp_response( const rtsp_message_t *request, const rtsp_message_t *response, const char *force_destination_ip, const apr_table_t *resource_map, apr_pool_t *pool, su_home_t *home) { mrcp_session_descriptor_t *descriptor = NULL; const char *resource_name = mrcp_name_get_by_rtsp_name( resource_map, request->start_line.common.request_line.resource_name); if(!resource_name) { return NULL; } if(request->start_line.common.request_line.method_id == RTSP_METHOD_SETUP) { if(rtsp_header_property_check(&response->header,RTSP_HEADER_FIELD_CONTENT_TYPE) == TRUE && rtsp_header_property_check(&response->header,RTSP_HEADER_FIELD_CONTENT_LENGTH) == TRUE && response->body.buf) { sdp_parser_t *parser; sdp_session_t *sdp; parser = sdp_parse(home,response->body.buf,response->body.length,0); sdp = sdp_session(parser); if(sdp) { descriptor = mrcp_session_descriptor_create(pool); mrcp_descriptor_generate_by_sdp_session(descriptor,sdp,force_destination_ip,pool); apt_string_assign(&descriptor->resource_name,resource_name,pool); descriptor->resource_state = TRUE; descriptor->response_code = response->start_line.common.status_line.status_code; } else { apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Failed to Parse SDP Message"); } sdp_parser_free(parser); } else { descriptor = mrcp_session_descriptor_create(pool); apt_string_assign(&descriptor->resource_name,resource_name,pool); descriptor->resource_state = FALSE; } } else if(request->start_line.common.request_line.method_id == RTSP_METHOD_TEARDOWN) { descriptor = mrcp_session_descriptor_create(pool); apt_string_assign(&descriptor->resource_name,resource_name,pool); descriptor->resource_state = FALSE; } return descriptor; }
/* v=0 o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4 s=SDP Seminar i=A Seminar on the session description protocol u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps [email protected] (Mark Handley) c=IN IP4 224.2.17.12/127 t=2873397496 2873404696 a=recvonly m=audio 3456 RTP/AVP 0 m=video 2232 RTP/AVP 31 m=whiteboard 32416 UDP WB a=orient:portrait */ int rtsp_client_sdp(struct rtsp_client_t* rtsp, const char* content) { int i, j, n, count; int formats[N_MEDIA_FORMAT]; struct rtsp_media_t* media; void* sdp; sdp = sdp_parse(content); if (!sdp) return -1; count = sdp_media_count(sdp); if(count > N_MEDIA) { rtsp->media_ptr = (struct rtsp_media_t*)malloc(sizeof(struct rtsp_media_t)*(count-N_MEDIA)); if(!rtsp->media_ptr) { sdp_destroy(sdp); return ENOMEM; } memset(rtsp->media_ptr, 0, sizeof(struct rtsp_media_t)*(count-N_MEDIA)); } rtsp->media_count = count; // rfc 2326 C.1.1 Control URL (p80) // If found at the session level, the attribute indicates the URL for aggregate control rtsp->aggregate = rtsp_media_aggregate_control_enable(sdp); rtsp_get_session_uri(sdp, rtsp->aggregate_uri, sizeof(rtsp->aggregate_uri), rtsp->uri, rtsp->baseuri, rtsp->location); for(i = 0; i < count; i++) { media = rtsp_get_media(rtsp, i); //media->cseq = rand(); // RTSP2326 C.1.1 Control URL rtsp_get_media_uri(sdp, i, media->uri, sizeof(media->uri), rtsp->aggregate_uri); n = sdp_media_formats(sdp, i, formats, N_MEDIA_FORMAT); media->avformat_count = n > N_MEDIA_FORMAT ? N_MEDIA_FORMAT : n; for(j = 0; j < media->avformat_count; j++) { media->avformats[j].fmt = formats[j]; } // update media encoding sdp_media_attribute_list(sdp, i, NULL, rtsp_media_onattr, media); } sdp_destroy(sdp); return 0; }
UniquePtr<Sdp> SipccSdpParser::Parse(const std::string &sdpText) { ClearParseErrors(); sdp_conf_options_t *sipcc_config = sdp_init_config(); if (!sipcc_config) { return UniquePtr<Sdp>(); } sdp_nettype_supported(sipcc_config, SDP_NT_INTERNET, true); sdp_addrtype_supported(sipcc_config, SDP_AT_IP4, true); sdp_addrtype_supported(sipcc_config, SDP_AT_IP6, true); sdp_transport_supported(sipcc_config, SDP_TRANSPORT_RTPAVP, true); sdp_transport_supported(sipcc_config, SDP_TRANSPORT_RTPAVPF, true); sdp_transport_supported(sipcc_config, SDP_TRANSPORT_RTPSAVP, true); sdp_transport_supported(sipcc_config, SDP_TRANSPORT_RTPSAVPF, true); sdp_transport_supported(sipcc_config, SDP_TRANSPORT_UDPTLSRTPSAVP, true); sdp_transport_supported(sipcc_config, SDP_TRANSPORT_UDPTLSRTPSAVPF, true); sdp_transport_supported(sipcc_config, SDP_TRANSPORT_TCPTLSRTPSAVP, true); sdp_transport_supported(sipcc_config, SDP_TRANSPORT_TCPTLSRTPSAVPF, true); sdp_transport_supported(sipcc_config, SDP_TRANSPORT_DTLSSCTP, true); sdp_require_session_name(sipcc_config, false); sdp_config_set_error_handler(sipcc_config, &sipcc_sdp_parser_error_handler, this); // Takes ownership of |sipcc_config| iff it succeeds sdp_t *sdp = sdp_init_description(sipcc_config); if (!sdp) { sdp_free_config(sipcc_config); return UniquePtr<Sdp>(); } const char *rawString = sdpText.c_str(); sdp_result_e sdpres = sdp_parse(sdp, rawString, sdpText.length()); if (sdpres != SDP_SUCCESS) { sdp_free_description(sdp); return UniquePtr<Sdp>(); } UniquePtr<SipccSdp> sipccSdp(new SipccSdp); bool success = sipccSdp->Load(sdp, *this); sdp_free_description(sdp); if (!success) { return UniquePtr<Sdp>(); } return UniquePtr<Sdp>(Move(sipccSdp)); }
static void mrcp_sofia_on_call_receive(mrcp_sofia_agent_t *sofia_agent, nua_handle_t *nh, mrcp_sofia_session_t *sofia_session, sip_t const *sip, tagi_t tags[]) { apt_bool_t status = FALSE; const char *remote_sdp_str = NULL; mrcp_session_descriptor_t *descriptor; if(!sofia_session) { sofia_session = mrcp_sofia_session_create(sofia_agent,nh); if(!sofia_session) { nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); return; } } descriptor = mrcp_session_descriptor_create(sofia_session->session->pool); tl_gets(tags, SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str), TAG_END()); if(remote_sdp_str) { sdp_parser_t *parser = NULL; sdp_session_t *sdp = NULL; apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP "APT_NAMESID_FMT"\n%s", sofia_session->session->name, MRCP_SESSION_SID(sofia_session->session), remote_sdp_str); parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0); sdp = sdp_session(parser); status = mrcp_descriptor_generate_by_sdp_session(descriptor,sdp,NULL,sofia_session->session->pool); sdp_parser_free(parser); } if(status == FALSE) { nua_respond(nh, SIP_400_BAD_REQUEST, TAG_END()); return; } mrcp_session_offer(sofia_session->session,descriptor); }
static void mrcp_sofia_on_call_receive(mrcp_sofia_agent_t *sofia_agent, nua_handle_t *nh, mrcp_sofia_session_t *sofia_session, sip_t const *sip, tagi_t tags[]) { int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0; const char *local_sdp_str = NULL, *remote_sdp_str = NULL; mrcp_session_descriptor_t *descriptor = NULL; tl_gets(tags, NUTAG_OFFER_RECV_REF(offer_recv), NUTAG_ANSWER_RECV_REF(answer_recv), NUTAG_OFFER_SENT_REF(offer_sent), NUTAG_ANSWER_SENT_REF(answer_sent), SOATAG_LOCAL_SDP_STR_REF(local_sdp_str), SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str), TAG_END()); if(!sofia_session) { sofia_session = mrcp_sofia_session_create(sofia_agent,nh); if(!sofia_session) { nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); return; } } if(remote_sdp_str) { sdp_parser_t *parser = NULL; sdp_session_t *sdp = NULL; apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP\n%s", remote_sdp_str); parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0); sdp = sdp_session(parser); descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,NULL,sofia_session->session->pool); sdp_parser_free(parser); } if(!descriptor) { nua_respond(nh, SIP_400_BAD_REQUEST, TAG_END()); return; } mrcp_session_offer(sofia_session->session,descriptor); }
/** Generate resource discovery descriptor by RTSP response */ MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_resource_discovery_response_generate( const rtsp_message_t *request, const rtsp_message_t *response, const apr_table_t *resource_map, apr_pool_t *pool, su_home_t *home) { mrcp_session_descriptor_t *descriptor = NULL; const char *resource_name = mrcp_name_get_by_rtsp_name( resource_map, request->start_line.common.request_line.resource_name); if(!resource_name) { return NULL; } descriptor = mrcp_session_descriptor_create(pool); apt_string_assign(&descriptor->resource_name,resource_name,pool); if(rtsp_header_property_check(&response->header,RTSP_HEADER_FIELD_CONTENT_TYPE) == TRUE && rtsp_header_property_check(&response->header,RTSP_HEADER_FIELD_CONTENT_LENGTH) == TRUE && response->body.buf) { sdp_parser_t *parser; sdp_session_t *sdp; parser = sdp_parse(home,response->body.buf,response->body.length,0); sdp = sdp_session(parser); if(sdp) { mrcp_descriptor_generate_by_sdp_session(descriptor,sdp,0,pool); descriptor->resource_state = TRUE; descriptor->response_code = response->start_line.common.status_line.status_code; } else { apt_string_assign(&descriptor->resource_name,resource_name,pool); descriptor->resource_state = TRUE; } sdp_parser_free(parser); } else { descriptor->resource_state = FALSE; } return descriptor; }
/** Generate MRCP descriptor by RTSP response */ MRCP_DECLARE(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_rtsp_response(const rtsp_message_t *request, const rtsp_message_t *response, apr_pool_t *pool, su_home_t *home) { mrcp_session_descriptor_t *descriptor = NULL; const char *resource_name = request->start_line.common.request_line.resource_name; if(!resource_name) { return NULL; } if(request->start_line.common.request_line.method_id == RTSP_METHOD_SETUP) { if(rtsp_header_property_check(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_TYPE) == TRUE && rtsp_header_property_check(&response->header.property_set,RTSP_HEADER_FIELD_CONTENT_LENGTH) == TRUE && response->body.buf) { sdp_parser_t *parser; sdp_session_t *sdp; parser = sdp_parse(home,response->body.buf,response->body.length,0); sdp = sdp_session(parser); descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,pool); if(descriptor) { apt_string_assign(&descriptor->resource_name,resource_name,pool); descriptor->resource_state = TRUE; } sdp_parser_free(parser); } else { descriptor = mrcp_session_descriptor_create(pool); if(descriptor) { apt_string_assign(&descriptor->resource_name,resource_name,pool); descriptor->resource_state = FALSE; } } } else if(request->start_line.common.request_line.method_id == RTSP_METHOD_TEARDOWN) { descriptor = mrcp_session_descriptor_create(pool); if(descriptor) { apt_string_assign(&descriptor->resource_name,resource_name,pool); descriptor->resource_state = FALSE; } } return descriptor; }
/* Pre-parse SDP: is this SDP valid? how many audio/video lines? any features to take into account? */ janus_sdp *janus_sdp_preparse(const char *jsep_sdp, int *audio, int *video, int *data, int *bundle, int *rtcpmux, int *trickle) { if(!jsep_sdp || !audio || !video || !data || !bundle || !rtcpmux || !trickle) { JANUS_LOG(LOG_ERR, " Can't preparse, invalid arduments\n"); return NULL; } sdp_parser_t *parser = sdp_parse(home, jsep_sdp, strlen(jsep_sdp), 0); sdp_session_t *parsed_sdp = sdp_session(parser); if(!parsed_sdp) { JANUS_LOG(LOG_ERR, " Error parsing SDP? %s\n", sdp_parsing_error(parser)); sdp_parser_free(parser); /* Invalid SDP */ return NULL; } sdp_media_t *m = parsed_sdp->sdp_media; while(m) { if(m->m_type == sdp_media_audio && m->m_port > 0) { *audio = *audio + 1; } else if(m->m_type == sdp_media_video && m->m_port > 0) { *video = *video + 1; } m = m->m_next; } #ifdef HAVE_SCTP *data = (strstr(jsep_sdp, "DTLS/SCTP") && !strstr(jsep_sdp, " 0 DTLS/SCTP")) ? 1 : 0; /* FIXME This is a really hacky way of checking... */ #else *data = 0; #endif *bundle = strstr(jsep_sdp, "a=group:BUNDLE") ? 1 : 0; /* FIXME This is a really hacky way of checking... */ *rtcpmux = strstr(jsep_sdp, "a=rtcp-mux") ? 1 : 0; /* FIXME Should we make this check per-medium? */ //~ *trickle = (strstr(jsep_sdp, "trickle") || strstr(jsep_sdp, "google-ice") || strstr(jsep_sdp, "Mozilla")) ? 1 : 0; /* FIXME This is a really hacky way of checking... */ /* FIXME We're assuming trickle is always supported, see https://github.com/meetecho/janus-gateway/issues/83 */ *trickle = 1; janus_sdp *sdp = (janus_sdp *)calloc(1, sizeof(janus_sdp)); if(sdp == NULL) { JANUS_LOG(LOG_FATAL, "Memory error!\n"); return NULL; } sdp->parser = parser; sdp->sdp = parsed_sdp; return sdp; }
static void mrcp_sofia_on_session_ready( int status, mrcp_sofia_agent_t *sofia_agent, nua_handle_t *nh, mrcp_sofia_session_t *sofia_session, sip_t const *sip, tagi_t tags[]) { mrcp_session_t *session = sofia_session->session; if(session) { const char *local_sdp_str = NULL, *remote_sdp_str = NULL; mrcp_session_descriptor_t *descriptor = NULL; tl_gets(tags, SOATAG_LOCAL_SDP_STR_REF(local_sdp_str), SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str), TAG_END()); if(remote_sdp_str) { sdp_parser_t *parser = NULL; sdp_session_t *sdp = NULL; const char *force_destination_ip = NULL; apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP "APT_NAMESID_FMT"\n%s", session->name, MRCP_SESSION_SID(session), remote_sdp_str); parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0); sdp = sdp_session(parser); if(sofia_session->sip_settings->force_destination == TRUE) { force_destination_ip = sofia_session->sip_settings->server_ip; } descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,force_destination_ip,session->pool); sdp_parser_free(parser); } mrcp_session_answer(session,descriptor); } }
static int rtsp_read_header(AVFormatContext *s, AVFormatParameters *ap) { RTSPState *rt = s->priv_data; char host[1024], path[1024], tcpname[1024], cmd[2048]; URLContext *rtsp_hd; int port, i, j, ret, err; RTSPHeader reply1, *reply = &reply1; unsigned char *content = NULL; RTSPStream *rtsp_st; int protocol_mask; AVStream *st; /* extract hostname and port */ url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, path, sizeof(path), s->filename); if (port < 0) port = RTSP_DEFAULT_PORT; /* open the tcp connexion */ snprintf(tcpname, sizeof(tcpname), "tcp://%s:%d", host, port); if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) return AVERROR_IO; rt->rtsp_hd = rtsp_hd; rt->seq = 0; /* describe the stream */ snprintf(cmd, sizeof(cmd), "DESCRIBE %s RTSP/1.0\r\n" "Accept: application/sdp\r\n", s->filename); rtsp_send_cmd(s, cmd, reply, &content); if (!content) { err = AVERROR_INVALIDDATA; goto fail; } if (reply->status_code != RTSP_STATUS_OK) { err = AVERROR_INVALIDDATA; goto fail; } /* now we got the SDP description, we parse it */ ret = sdp_parse(s, (const char *)content); av_freep(&content); if (ret < 0) { err = AVERROR_INVALIDDATA; goto fail; } protocol_mask = rtsp_default_protocols; /* for each stream, make the setup request */ /* XXX: we assume the same server is used for the control of each RTSP stream */ for(j = RTSP_RTP_PORT_MIN, i = 0; i < rt->nb_rtsp_streams; ++i) { char transport[2048]; rtsp_st = rt->rtsp_streams[i]; /* compute available transports */ transport[0] = '\0'; /* RTP/UDP */ if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP)) { char buf[256]; /* first try in specified port range */ if (RTSP_RTP_PORT_MIN != 0) { while(j <= RTSP_RTP_PORT_MAX) { snprintf(buf, sizeof(buf), "rtp://?localport=%d", j); if (url_open(&rtsp_st->rtp_handle, buf, URL_RDONLY) == 0) { j += 2; /* we will use two port by rtp stream (rtp and rtcp) */ goto rtp_opened; } } } /* then try on any port ** if (url_open(&rtsp_st->rtp_handle, "rtp://", URL_RDONLY) < 0) { ** err = AVERROR_INVALIDDATA; ** goto fail; ** } */ rtp_opened: port = rtp_get_local_port(rtsp_st->rtp_handle); if (transport[0] != '\0') pstrcat(transport, sizeof(transport), ","); snprintf(transport + strlen(transport), sizeof(transport) - strlen(transport) - 1, "RTP/AVP/UDP;unicast;client_port=%d-%d", port, port + 1); } /* RTP/TCP */ else if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_TCP)) { if (transport[0] != '\0') pstrcat(transport, sizeof(transport), ","); snprintf(transport + strlen(transport), sizeof(transport) - strlen(transport) - 1, "RTP/AVP/TCP"); } else if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP_MULTICAST)) { if (transport[0] != '\0') pstrcat(transport, sizeof(transport), ","); snprintf(transport + strlen(transport), sizeof(transport) - strlen(transport) - 1, "RTP/AVP/UDP;multicast"); } snprintf(cmd, sizeof(cmd), "SETUP %s RTSP/1.0\r\n" "Transport: %s\r\n", rtsp_st->control_url, transport); rtsp_send_cmd(s, cmd, reply, NULL); if (reply->status_code != RTSP_STATUS_OK || reply->nb_transports != 1) { err = AVERROR_INVALIDDATA; goto fail; } /* XXX: same protocol for all streams is required */ if (i > 0) { if (reply->transports[0].protocol != rt->protocol) { err = AVERROR_INVALIDDATA; goto fail; } } else { rt->protocol = reply->transports[0].protocol; } /* close RTP connection if not choosen */ if (reply->transports[0].protocol != RTSP_PROTOCOL_RTP_UDP && (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP))) { url_close(rtsp_st->rtp_handle); rtsp_st->rtp_handle = NULL; } switch(reply->transports[0].protocol) { case RTSP_PROTOCOL_RTP_TCP: rtsp_st->interleaved_min = reply->transports[0].interleaved_min; rtsp_st->interleaved_max = reply->transports[0].interleaved_max; break; case RTSP_PROTOCOL_RTP_UDP: { char url[1024]; /* XXX: also use address if specified */ snprintf(url, sizeof(url), "rtp://%s:%d", host, reply->transports[0].server_port_min); if (rtp_set_remote_url(rtsp_st->rtp_handle, url) < 0) { err = AVERROR_INVALIDDATA; goto fail; } } break; case RTSP_PROTOCOL_RTP_UDP_MULTICAST: { char url[1024]; int ttl; ttl = reply->transports[0].ttl; if (!ttl) ttl = 16; snprintf(url, sizeof(url), "rtp://%s:%d?multicast=1&ttl=%d", host, reply->transports[0].server_port_min, ttl); if (url_open(&rtsp_st->rtp_handle, url, URL_RDONLY) < 0) { err = AVERROR_INVALIDDATA; goto fail; } } break; } /* open the RTP context */ st = NULL; if (rtsp_st->stream_index >= 0) st = s->streams[rtsp_st->stream_index]; if (!st) s->ctx_flags |= AVFMTCTX_NOHEADER; rtsp_st->rtp_ctx = rtp_parse_open(s, st, rtsp_st->sdp_payload_type, &rtsp_st->rtp_payload_data); if (!rtsp_st->rtp_ctx) { err = AVERROR_NOMEM; goto fail; } } /* use callback if available to extend setup */ if (ff_rtsp_callback) { if (ff_rtsp_callback(RTSP_ACTION_CLIENT_SETUP, rt->session_id, NULL, 0, rt->last_reply) < 0) { err = AVERROR_INVALIDDATA; goto fail; } } rt->state = RTSP_STATE_IDLE; rt->seek_timestamp = 0; /* default is to start stream at position zero */ if (ap->initial_pause) { /* do not start immediately */ } else { if (rtsp_read_play(s) < 0) { err = AVERROR_INVALIDDATA; goto fail; } } return 0; fail: rtsp_close_streams(rt); av_freep(&content); url_close(rt->rtsp_hd); return err; }
int main(int argc, char **argv){ /* create a rtp session, we will assign unique value to identify */ err = rtp_create(&sid); if (err) { err_handle(err); } sdp_parse(sid, argv); /* parse the sdp file */ /* The application will sleep "rtp_start_time" time if "rtp_start_time" > 0 when the application is start. * * NOTE! This is not the same with Start Time in NCTUns * EX: if the Start Time in NCTUn is 5, and rtp_start_time is 3, * then the real time to startup the applicaion is 8. */ if (rtp_start_time > 0) { usleep( ((int) rtp_start_time * 1000000) ); } initial_session(argc, argv, sid); /* set local receive addr, CNAME, and startup the connectoin */ if (!trans_mode) { /* throughput is static */ if (is_audio) /* media type is audio */ delay = (ms_pkt / 1000.); else delay = (1. / framerate); /* bytes of each packet = ((bits/sample) / 8 ) * (clock rate) * ( each delay of packet in sec ) */ packet_size = (int) ( (bits_per_sample / 8.) * sampling_rate * delay); if (RTP_MAX_PKT_SIZE < packet_size) { fprintf(stderr, "The packet size is bigger than RTP_MAX_PKT_SIZE\n"); exit(1); } } /* original_bw = min_bw = 50, so we use the same pkt size */ minbw_pktsize = packet_size; printf("bits_per_sample = %lf, (bits_per_sample/8.) = %lf\n", bits_per_sample, (bits_per_sample/8.)); printf("sampling_rate = %lf\n", sampling_rate); printf("delay = %lf\n", delay); printf("packet_size = %d\n", packet_size); /* the bandwidth wa are using, 30 = frames/sec */ cur_bw = get_cur_bw(packet_size); err = rtp_get_sour_rtpsocket(sid, &rtp_sockt); if (err) { err_handle(err); } err = rtp_get_sour_rtcpsocket(sid, &rtcp_sockt); if (err) { err_handle(err); } if (rtp_sockt > nfds || rtcp_sockt > nfds) { if (rtp_sockt > rtcp_sockt) nfds = rtp_sockt; else nfds = rtcp_sockt; } FD_ZERO(&afds); FD_ZERO(&rfds); FD_SET(rtp_sockt, &afds); FD_SET(rtcp_sockt, &afds); gettimeofday(&start_tv, NULL); starttime = (start_tv.tv_sec + start_tv.tv_usec / 1000000.); nexttime = starttime; rtp_interval = rtp_stop_time - rtp_start_time; printf("rtp_interval = %f\n", rtp_interval); fflush(stdout); while (rtp_interval >= 0) { memcpy(&rfds, &afds, sizeof(rfds)); if (RTP_MAX_PKT_SIZE < packet_size){ fprintf(stderr, "RTP_MAX_PKT_SIZE < reads\n"); continue; } addtimestamp = ((int) packet_size * (bits_per_sample / 8.) ); err = rtp_send(sid, marker, addtimestamp, codec_num, (int8 *)sendbuf, packet_size); if (err) { err_handle(err); } marker = 0; /* not the first packet of talkspurt */ rtp_interval -= delay; nexttime += delay; gettimeofday(&now_tv, NULL); now = (now_tv.tv_sec + now_tv.tv_usec / 1000000.); err = get_rtcp_timeout(sid, &timeout_tv); if (err) { err_handle(err); } while (now < nexttime) { /* send next packet until now >= nexttime */ //printf("now = %lf\n", now); //printf("nexttime = %lf\n", nexttime); if (time_expire(&timeout_tv, &now_tv)) { err = rtp_check_on_expire(); if (err) { err_handle(err); } err = get_rtcp_timeout(sid, &timeout_tv); if (err) { err_handle(err); } printf("timeval_to_double(timeout_tv) = %lf\n", timeval_to_double(timeout_tv)); } /* BECAREFUL, if we disable RTCP, the timeval we get will be 0 */ if (timeval_to_double(timeout_tv) == 0 || nexttime < timeval_to_double(timeout_tv)) { nexttime_tv = double_to_timeval(nexttime - now); } else { nexttime_tv = double_to_timeval(timeval_to_double(timeout_tv) - now); } if (select(nfds + 1, &rfds, (fd_set *)0, (fd_set *)0, &nexttime_tv) < 0) { if (errno == EINTR) continue; else { printf("nexttime_tv.tv_sec = %ld\n", nexttime_tv.tv_sec); printf("nexttime_tv.tv_usec = %ld\n", nexttime_tv.tv_usec); printf("select error: %d\n", errno); //exit(1); } } if (FD_ISSET(rtp_sockt, &rfds)) { err = on_receive(sid, rtp_sockt, recvbuf, &recbuflen); if (err) { err_handle(err); } } else if (FD_ISSET(rtcp_sockt, &rfds)) { err = on_receive(sid, rtcp_sockt, recvbuf, &recbuflen); if (err) { err_handle(err); } adapt_bw(get_recent_recv_loosrate(sid, &ssrc)); } gettimeofday(&now_tv, NULL); now = (now_tv.tv_sec + now_tv.tv_usec / 1000000.); } // while(now < nexttime) } // while (interval) err = rtp_close_connection(sid, reason); if (err) { err_handle(err); } err = rtp_delete(sid); if (err) { err_handle(err); } return 0; }
/* * [ application_name local_ip local_port cname sdp_file [-t trace_file] ] */ int main(int argc, char **argv){ /* create a rtp session, we will assign unique value to identify */ err = rtp_create(&sid); if (err) { err_handle(err); } sdp_parse(sid, argv); /* parse the sdp file */ /* The application will sleep "rtp_start_time" time if "rtp_start_time" > 0 when the application is start. * * NOTE! This is not the same with Start Time in NCTUns * EX: if the Start Time in NCTUn is 5, and rtp_start_time is 3, * then the real time to startup the applicaion is 8. */ if (rtp_start_time > 0) usleep( ((int) rtp_start_time * 1000000) ); initial_session(argc, argv, sid); /* set local receive addr, CNAME, and startup the connectoin */ if (is_audio) /* media type is audio */ delay = (ms_pkt / 1000.); else delay = (1. / framerate); err = rtp_get_sour_rtpsocket(sid, &rtp_sockt); /* get source rtp socket */ if (err) err_handle(err); err = rtp_get_sour_rtcpsocket(sid, &rtcp_sockt); /* get source rtcp socket */ if (err) err_handle(err); if (rtp_sockt > nfds || rtcp_sockt > nfds) { if (rtp_sockt > rtcp_sockt) nfds = rtp_sockt; else nfds = rtcp_sockt; } FD_ZERO(&afds); FD_ZERO(&rfds); FD_SET(rtp_sockt, &afds); FD_SET(rtcp_sockt, &afds); gettimeofday(&start_tv, NULL); starttime = (start_tv.tv_sec + start_tv.tv_usec / 1000000.); now = starttime; rtp_interval = rtp_stop_time - rtp_start_time; /* bytes of each packet = ((bits/sample) / 8 ) * (clock rate) * ( each delay of packet in sec ) */ // packet_size = (int) ( (bits_per_sample / 8.) * sampling_rate * delay); while ((now - starttime) <= rtp_interval) { memcpy(&rfds, &afds, sizeof(rfds)); err = get_rtcp_timeout(sid, &timeout_tv); /* get the send_rtcp_packet time */ if (err) err_handle(err); if (time_expire(&timeout_tv, &now_tv)) { err = rtp_check_on_expire(); /* rtcp on_expire */ if (err) err_handle(err); err = get_rtcp_timeout(sid, &timeout_tv); /* get the send_rtcp_packet time */ if (err) err_handle(err); } nexttime_tv = double_to_timeval(timeval_to_double(timeout_tv) - now); if (select(nfds + 1, &rfds, (fd_set *)0, (fd_set *)0, &nexttime_tv) < 0) { if (errno == EINTR) continue; else { printf("nexttime_tv.tv_sec = %ld\n", nexttime_tv.tv_sec); printf("nexttime_tv.tv_usec = %ld\n", nexttime_tv.tv_usec); printf("select error: %d\n", errno); } } if (FD_ISSET(rtp_sockt, &rfds)) { err = on_receive(sid, rtp_sockt, recvbuf, &recbuflen); if (err) err_handle(err); } else if (FD_ISSET(rtcp_sockt, &rfds)) { err = on_receive(sid, rtcp_sockt, recvbuf, &recbuflen); if (err) err_handle(err); } gettimeofday(&now_tv, NULL); now = (now_tv.tv_sec + now_tv.tv_usec / 1000000.); } // while ((now - starttime) <= rtp_interval) err = rtp_close_connection(sid, reason); if (err) { err_handle(err); } err = rtp_delete(sid); if (err) { err_handle(err); } return 0; }
void ParseSdp(const std::string &sdp_str) { char *bufp = const_cast<char *>(sdp_str.data()); ResetSdp(); ASSERT_EQ(sdp_parse(sdp_ptr_, &bufp, sdp_str.size()), SDP_SUCCESS); }
static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output, enum call_opmode opmode) { str sdp, fromtag, totag = STR_NULL, callid; char *errstr; GQueue parsed = G_QUEUE_INIT; GQueue streams = G_QUEUE_INIT; struct call *call; struct call_monologue *monologue; int ret; struct sdp_ng_flags flags; struct sdp_chopper *chopper; if (!bencode_dictionary_get_str(input, "sdp", &sdp)) return "No SDP body in message"; if (!bencode_dictionary_get_str(input, "call-id", &callid)) return "No call-id in message"; if (!bencode_dictionary_get_str(input, "from-tag", &fromtag)) return "No from-tag in message"; if (opmode == OP_ANSWER) { if (!bencode_dictionary_get_str(input, "to-tag", &totag)) return "No to-tag in message"; } //bencode_dictionary_get_str(input, "via-branch", &viabranch); if (sdp_parse(&sdp, &parsed)) return "Failed to parse SDP"; call_ng_process_flags(&flags, input); flags.opmode = opmode; errstr = "Incomplete SDP specification"; if (sdp_streams(&parsed, &streams, &flags)) goto out; call = call_get_opmode(&callid, m, opmode); errstr = "Unknown call-id"; if (!call) goto out; /* At least the random ICE strings are contained within the call struct, so we * need to hold a ref until we're done sending the reply */ call_bencode_hold_ref(call, output); monologue = call_get_mono_dialogue(call, &fromtag, &totag); errstr = "Invalid dialogue association"; if (!monologue) { rwlock_unlock_w(&call->master_lock); obj_put(call); goto out; } chopper = sdp_chopper_new(&sdp); bencode_buffer_destroy_add(output->buffer, (free_func_t) sdp_chopper_destroy, chopper); ret = monologue_offer_answer(monologue, &streams, &flags); if (!ret) ret = sdp_replace(chopper, &parsed, monologue->active_dialogue, &flags); rwlock_unlock_w(&call->master_lock); redis_update(call, m->conf.redis); obj_put(call); errstr = "Error rewriting SDP"; if (ret) goto out; bencode_dictionary_add_iovec(output, "sdp", &g_array_index(chopper->iov, struct iovec, 0), chopper->iov_num, chopper->str_len); bencode_dictionary_add_string(output, "result", "ok"); errstr = NULL; out: sdp_free(&parsed); streams_free(&streams); return errstr; }
char *janus_sdp_anonymize(const char *sdp) { if(sdp == NULL) return NULL; //~ su_home_t home[1] = { SU_HOME_INIT(home) }; sdp_session_t *anon = NULL; sdp_parser_t *parser = sdp_parse(home, sdp, strlen(sdp), 0); if(!(anon = sdp_session(parser))) { JANUS_DEBUG("Error parsing/merging SDP: %s\n", sdp_parsing_error(parser)); return NULL; } //~ /* o= */ //~ if(anon->sdp_origin && anon->sdp_origin->o_username) { //~ free(anon->sdp_origin->o_username); //~ anon->sdp_origin->o_username = strdup("JANUS"); //~ } /* c= */ if(anon->sdp_connection && anon->sdp_connection->c_address) { //~ free(anon->sdp_connection->c_address); anon->sdp_connection->c_address = strdup("1.1.1.1"); } /* a= */ if(anon->sdp_attributes) { /* FIXME These are attributes we handle ourselves, the plugins don't need them */ while(sdp_attribute_find(anon->sdp_attributes, "ice-ufrag")) sdp_attribute_remove(&anon->sdp_attributes, "ice-ufrag"); while(sdp_attribute_find(anon->sdp_attributes, "ice-pwd")) sdp_attribute_remove(&anon->sdp_attributes, "ice-pwd"); while(sdp_attribute_find(anon->sdp_attributes, "ice-options")) sdp_attribute_remove(&anon->sdp_attributes, "ice-options"); while(sdp_attribute_find(anon->sdp_attributes, "fingerprint")) sdp_attribute_remove(&anon->sdp_attributes, "fingerprint"); while(sdp_attribute_find(anon->sdp_attributes, "group")) sdp_attribute_remove(&anon->sdp_attributes, "group"); while(sdp_attribute_find(anon->sdp_attributes, "msid-semantic")) sdp_attribute_remove(&anon->sdp_attributes, "msid-semantic"); } /* m= */ int a_sendrecv = 0, v_sendrecv = 0; if(anon->sdp_media) { int audio = 0, video = 0; sdp_media_t *m = anon->sdp_media; while(m) { if(m->m_type == sdp_media_audio) { audio++; m->m_port = audio == 1 ? 1 : 0; } else if(m->m_type == sdp_media_video) { video++; m->m_port = audio == 1 ? 1 : 0; } else { m->m_port = 0; } /* c= */ if(m->m_connections) { sdp_connection_t *c = m->m_connections; while(c) { if(c->c_address) { //~ free(c->c_address); c->c_address = strdup("1.1.1.1"); } c = c->c_next; } } /* a= */ if(m->m_attributes) { /* FIXME These are attributes we handle ourselves, the plugins don't need them */ while(sdp_attribute_find(m->m_attributes, "ice-ufrag")) sdp_attribute_remove(&m->m_attributes, "ice-ufrag"); while(sdp_attribute_find(m->m_attributes, "ice-pwd")) sdp_attribute_remove(&m->m_attributes, "ice-pwd"); while(sdp_attribute_find(m->m_attributes, "ice-options")) sdp_attribute_remove(&m->m_attributes, "ice-options"); while(sdp_attribute_find(m->m_attributes, "crypto")) sdp_attribute_remove(&m->m_attributes, "crypto"); while(sdp_attribute_find(m->m_attributes, "fingerprint")) sdp_attribute_remove(&m->m_attributes, "fingerprint"); while(sdp_attribute_find(m->m_attributes, "setup")) sdp_attribute_remove(&m->m_attributes, "setup"); while(sdp_attribute_find(m->m_attributes, "connection")) sdp_attribute_remove(&m->m_attributes, "connection"); while(sdp_attribute_find(m->m_attributes, "group")) sdp_attribute_remove(&m->m_attributes, "group"); while(sdp_attribute_find(m->m_attributes, "msid-semantic")) sdp_attribute_remove(&m->m_attributes, "msid-semantic"); while(sdp_attribute_find(m->m_attributes, "rtcp")) sdp_attribute_remove(&m->m_attributes, "rtcp"); while(sdp_attribute_find(m->m_attributes, "rtcp-mux")) sdp_attribute_remove(&m->m_attributes, "rtcp-mux"); while(sdp_attribute_find(m->m_attributes, "candidate")) sdp_attribute_remove(&m->m_attributes, "candidate"); while(sdp_attribute_find(m->m_attributes, "ssrc")) sdp_attribute_remove(&m->m_attributes, "ssrc"); while(sdp_attribute_find(m->m_attributes, "extmap")) /* TODO Actually implement RTP extensions */ sdp_attribute_remove(&m->m_attributes, "extmap"); } /* FIXME sendrecv hack: sofia-sdp doesn't print sendrecv, but we want it to */ if(m->m_mode == sdp_sendrecv) { m->m_mode = sdp_inactive; if(m->m_type == sdp_media_audio) a_sendrecv = 1; else if(m->m_type == sdp_media_video) v_sendrecv = 1; } m = m->m_next; } } char buf[BUFSIZE]; sdp_printer_t *printer = sdp_print(home, anon, buf, BUFSIZE, 0); if(sdp_message(printer)) { int retval = sdp_message_size(printer); sdp_printer_free(printer); /* FIXME Take care of the sendrecv hack */ if(a_sendrecv || v_sendrecv) { char *replace = strstr(buf, "a=inactive"); while(replace != NULL) { memcpy(replace, "a=sendrecv", strlen("a=sendrecv")); replace++; replace = strstr(replace, "a=inactive"); } } JANUS_PRINT(" -------------------------------------------\n"); JANUS_PRINT(" >> Anonymized (%zu --> %d bytes)\n", strlen(sdp), retval); JANUS_PRINT(" -------------------------------------------\n"); JANUS_PRINT("%s\n", buf); return g_strdup(buf); } else { JANUS_DEBUG("Error anonymizing SDP: %s\n", sdp_printing_error(printer)); return NULL; } }
char *janus_sdp_merge(janus_ice_handle *handle, const char *origsdp) { if(handle == NULL || origsdp == NULL) return NULL; //~ su_home_t home[1] = { SU_HOME_INIT(home) }; sdp_session_t *anon = NULL; sdp_parser_t *parser = sdp_parse(home, origsdp, strlen(origsdp), 0); if(!(anon = sdp_session(parser))) { JANUS_DEBUG("[%"SCNu64"] Error parsing/merging SDP: %s\n", handle->handle_id, sdp_parsing_error(parser)); return NULL; } /* Prepare SDP to merge */ gchar buffer[200]; memset(buffer, 0, 200); char *sdp = (char*)calloc(BUFSIZE, sizeof(char)); if(sdp == NULL) { JANUS_DEBUG("Memory error!\n"); return NULL; } sdp[0] = '\0'; /* Version v= */ g_strlcat(sdp, "v=0\r\n", BUFSIZE); /* Origin o= */ if(anon->sdp_origin) { g_sprintf(buffer, "o=%s %"SCNu64" %"SCNu64" IN IP4 127.0.0.1\r\n", /* FIXME Should we fix the address? */ anon->sdp_origin->o_username ? anon->sdp_origin->o_username : "******", anon->sdp_origin->o_id, anon->sdp_origin->o_version); g_strlcat(sdp, buffer, BUFSIZE); } else { gint64 sessid = g_get_monotonic_time(); gint64 version = sessid; /* FIXME This needs to be increased when it changes, so time should be ok */ g_sprintf(buffer, "o=%s %"SCNi64" %"SCNi64" IN IP4 127.0.0.1\r\n", /* FIXME Should we fix the address? */ "-", sessid, version); g_strlcat(sdp, buffer, BUFSIZE); } /* Session name s= */ g_sprintf(buffer, "s=%s\r\n", anon->sdp_subject ? anon->sdp_subject : "Meetecho Janus"); g_strlcat(sdp, buffer, BUFSIZE); /* Timing t= */ g_sprintf(buffer, "t=%lu %lu\r\n", anon->sdp_time ? anon->sdp_time->t_start : 0, anon->sdp_time ? anon->sdp_time->t_stop : 0); g_strlcat(sdp, buffer, BUFSIZE); /* Any global bandwidth? */ //~ if(anon->sdp_bandwidths) { //~ g_sprintf(buffer, //~ "b=%s:%"SCNu64"\r\n", //~ anon->sdp_bandwidths->b_modifier_name ? anon->sdp_bandwidths->b_modifier_name : "AS", //~ anon->sdp_bandwidths->b_value); //~ g_strlcat(sdp, buffer, BUFSIZE); //~ } /* msid-semantic: add new global attribute */ g_strlcat(sdp, "a=msid-semantic: WMS janus\r\n", BUFSIZE); //~ /* Connection c= (global) */ //~ if(anon->sdp_connection) { //~ g_sprintf(buffer, //~ "c=IN IP4 %s\r\n", janus_get_local_ip()); //~ g_strlcat(sdp, buffer, BUFSIZE); //~ } /* DTLS fingerprint a= (global) */ g_sprintf(buffer, "a=fingerprint:sha-256 %s\r\n", janus_dtls_get_local_fingerprint()); g_strlcat(sdp, buffer, BUFSIZE); /* Copy other global attributes, if any */ if(anon->sdp_attributes) { sdp_attribute_t *a = anon->sdp_attributes; while(a) { if(a->a_value == NULL) { g_sprintf(buffer, "a=%s\r\n", a->a_name); g_strlcat(sdp, buffer, BUFSIZE); } else { g_sprintf(buffer, "a=%s:%s\r\n", a->a_name, a->a_value); g_strlcat(sdp, buffer, BUFSIZE); } a = a->a_next; } } /* Media lines now */ if(anon->sdp_media) { int audio = 0, video = 0; sdp_media_t *m = anon->sdp_media; janus_ice_stream *stream = NULL; while(m) { if(m->m_type == sdp_media_audio) { audio++; if(audio > 1 || !handle->audio_id) { JANUS_DEBUG("[%"SCNu64"] Skipping audio line (we have %d audio lines, and the id is %d)\n", handle->handle_id, audio, handle->audio_id); g_strlcat(sdp, "m=audio 0 RTP/SAVPF 0\r\n", BUFSIZE); m = m->m_next; continue; } /* Audio */ stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id)); if(stream == NULL) { JANUS_DEBUG("[%"SCNu64"] Skipping audio line (invalid stream %d)\n", handle->handle_id, handle->audio_id); g_strlcat(sdp, "m=audio 0 RTP/SAVPF 0\r\n", BUFSIZE); m = m->m_next; continue; } g_strlcat(sdp, "m=audio ARTPP RTP/SAVPF", BUFSIZE); } else if(m->m_type == sdp_media_video) { video++; if(video > 1 || !handle->video_id) { JANUS_DEBUG("[%"SCNu64"] Skipping video line (we have %d video lines, and the id is %d)\n", handle->handle_id, video, handle->video_id); g_strlcat(sdp, "m=video 0 RTP/SAVPF 0\r\n", BUFSIZE); m = m->m_next; continue; } /* Video */ stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->video_id)); if(stream == NULL) { JANUS_DEBUG("[%"SCNu64"] Skipping video line (invalid stream %d)\n", handle->handle_id, handle->audio_id); g_strlcat(sdp, "m=video 0 RTP/SAVPF 0\r\n", BUFSIZE); m = m->m_next; continue; } g_strlcat(sdp, "m=video VRTPP RTP/SAVPF", BUFSIZE); } else { JANUS_DEBUG("[%"SCNu64"] Skipping unsupported media line...\n", handle->handle_id); g_sprintf(buffer, "m=%s 0 %s 0\r\n", m->m_type_name, m->m_proto_name); g_strlcat(sdp, buffer, BUFSIZE); m = m->m_next; continue; } /* Add formats now */ if(!m->m_rtpmaps) { JANUS_PRINT("[%"SCNu64"] No RTP maps?? trying formats...\n", handle->handle_id); if(!m->m_format) { JANUS_DEBUG("[%"SCNu64"] No formats either?? this sucks!\n", handle->handle_id); g_strlcat(sdp, " 0", BUFSIZE); /* FIXME Won't work apparently */ } else { sdp_list_t *fmt = m->m_format; while(fmt) { g_sprintf(buffer, " %s", fmt->l_text); g_strlcat(sdp, buffer, BUFSIZE); fmt = fmt->l_next; } } } else { sdp_rtpmap_t *r = m->m_rtpmaps; while(r) { g_sprintf(buffer, " %d", r->rm_pt); g_strlcat(sdp, buffer, BUFSIZE); r = r->rm_next; } } g_strlcat(sdp, "\r\n", BUFSIZE); /* Any bandwidth? */ if(m->m_bandwidths) { g_sprintf(buffer, "b=%s:%lu\r\n", /* FIXME Are we doing this correctly? */ m->m_bandwidths->b_modifier_name ? m->m_bandwidths->b_modifier_name : "AS", m->m_bandwidths->b_value); g_strlcat(sdp, buffer, BUFSIZE); } /* Media connection c= */ //~ if(m->m_connections) { g_sprintf(buffer, "c=IN IP4 %s\r\n", janus_get_local_ip()); g_strlcat(sdp, buffer, BUFSIZE); //~ } /* What is the direction? */ switch(m->m_mode) { case sdp_inactive: g_strlcat(sdp, "a=inactive\r\n", BUFSIZE); break; case sdp_sendonly: g_strlcat(sdp, "a=sendonly\r\n", BUFSIZE); break; case sdp_recvonly: g_strlcat(sdp, "a=recvonly\r\n", BUFSIZE); break; case sdp_sendrecv: default: g_strlcat(sdp, "a=sendrecv\r\n", BUFSIZE); break; } /* RTCP */ g_sprintf(buffer, "a=rtcp:%s IN IP4 %s\r\n", m->m_type == sdp_media_audio ? "ARTCP" : "VRTCP", janus_get_local_ip()); g_strlcat(sdp, buffer, BUFSIZE); /* RTP maps */ if(m->m_rtpmaps) { sdp_rtpmap_t *rm = NULL; for(rm = m->m_rtpmaps; rm; rm = rm->rm_next) { g_sprintf(buffer, "a=rtpmap:%u %s/%lu%s%s\r\n", rm->rm_pt, rm->rm_encoding, rm->rm_rate, rm->rm_params ? "/" : "", rm->rm_params ? rm->rm_params : ""); g_strlcat(sdp, buffer, BUFSIZE); } for(rm = m->m_rtpmaps; rm; rm = rm->rm_next) { if(rm->rm_fmtp) { g_sprintf(buffer, "a=fmtp:%u %s\r\n", rm->rm_pt, rm->rm_fmtp); g_strlcat(sdp, buffer, BUFSIZE); } } } /* ICE ufrag and pwd, DTLS setup and connection a= */ gchar *ufrag = NULL; gchar *password = NULL; nice_agent_get_local_credentials(handle->agent, stream->stream_id, &ufrag, &password); memset(buffer, 0, 100); g_sprintf(buffer, "a=ice-ufrag:%s\r\n" "a=ice-pwd:%s\r\n" "a=setup:%s\r\n" "a=connection:new\r\n", ufrag, password, janus_get_dtls_srtp_role(stream->dtls_role)); g_strlcat(sdp, buffer, BUFSIZE); /* Copy existing media attributes, if any */ if(m->m_attributes) { sdp_attribute_t *a = m->m_attributes; while(a) { if(a->a_value == NULL) { g_sprintf(buffer, "a=%s\r\n", a->a_name); g_strlcat(sdp, buffer, BUFSIZE); } else { g_sprintf(buffer, "a=%s:%s\r\n", a->a_name, a->a_value); g_strlcat(sdp, buffer, BUFSIZE); } a = a->a_next; } } /* Add last attributes, rtcp and ssrc (msid) */ if(m->m_type == sdp_media_audio) { g_sprintf(buffer, //~ "a=rtcp:ARTCP IN IP4 %s\r\n" "a=ssrc:%i cname:janusaudio\r\n" "a=ssrc:%i msid:janus janusa0\r\n" "a=ssrc:%i mslabel:janus\r\n" "a=ssrc:%i label:janusa0\r\n", //~ janus_get_local_ip(), stream->ssrc, stream->ssrc, stream->ssrc, stream->ssrc); g_strlcat(sdp, buffer, BUFSIZE); } else if(m->m_type == sdp_media_video) { g_sprintf(buffer, //~ "a=rtcp:VRTCP IN IP4 %s\r\n" "a=ssrc:%i cname:janusvideo\r\n" "a=ssrc:%i msid:janus janusv0\r\n" "a=ssrc:%i mslabel:janus\r\n" "a=ssrc:%i label:janusv0\r\n", //~ janus_get_local_ip(), stream->ssrc, stream->ssrc, stream->ssrc, stream->ssrc); g_strlcat(sdp, buffer, BUFSIZE); } /* And now the candidates */ janus_ice_setup_candidate(handle, sdp, stream->stream_id, 1); janus_ice_setup_candidate(handle, sdp, stream->stream_id, 2); /* Next */ m = m->m_next; } } JANUS_PRINT(" -------------------------------------------\n"); JANUS_PRINT(" >> Merged (%zu --> %zu bytes)\n", strlen(origsdp), strlen(sdp)); JANUS_PRINT(" -------------------------------------------\n"); JANUS_PRINT("%s\n", sdp); return sdp; }
int main(int argc, char **argv){ /* setting commad line information */ local_ip = argv[1]; local_port = atoi(argv[2]); cname = argv[3]; proto = udp; /* create a rtp session, we will assign unique value to identify */ err = rtp_create(&sid); if (err) { err_handle(err); } sdp_parse(sid, argv); if (rtp_start_time > 0) { usleep( ((int) rtp_start_time * 1000000) ); } if ((i = initial_session(argc, argv, sid))) { printf("WARNING : initial_session warning = %d\n", i); } else { printf("initial_session is ok\n"); } if (is_audio) { delay = (ms_pkt / 1000.); } else { delay = (1. / framerate); // unit: ms/sec. we assume that 300 samples/frame, } /* bytes of each packet = ((bits/sample) / 8 ) * (clock rate) * ( each delay of packet in sec ) */ packet_size = (int) ( bits_per_sample * sampling_rate * delay / 8.); /* original_bw = min_bw = 50, so we use the same pkt size */ minbw_pktsize = packet_size; printf("bits_per_sample = %lf, (bits_per_sample/8.) = %lf\n", bits_per_sample, (bits_per_sample/8.)); printf("sampling_rate = %lf\n", sampling_rate); printf("delay = %lf\n", delay); printf("packet_size = %d\n", packet_size); fflush(stdout); /* the bandwidth wa are using, 30 = frames/sec */ cur_bw = get_cur_bw(packet_size); err = rtp_get_sour_rtpsocket(sid, &rtp_sockt); if (err) { err_handle(err); } err = rtp_get_sour_rtcpsocket(sid, &rtcp_sockt); if (err) { err_handle(err); } if (rtp_sockt > nfds || rtcp_sockt > nfds) { if (rtp_sockt > rtcp_sockt) nfds = rtp_sockt; else nfds = rtcp_sockt; } FD_ZERO(&afds); FD_ZERO(&rfds); FD_SET(rtp_sockt, &afds); FD_SET(rtcp_sockt, &afds); gettimeofday(&start_tv, NULL); starttime = (start_tv.tv_sec + start_tv.tv_usec / 1000000.); nexttime = starttime; rtp_interval = rtp_stop_time - rtp_start_time; printf("rtp_interval = %f\n", rtp_interval); fflush(stdout); while (rtp_interval >= 0) { memcpy(&rfds, &afds, sizeof(rfds)); if (RTP_MAX_PKT_SIZE < packet_size){ fprintf(stderr, "RTP_MAX_PKT_SIZE < reads\n"); continue; } addtimestamp = ((int) packet_size * (bits_per_sample / 8.) ); err = rtp_send(sid, marker, addtimestamp, codec_num, (int8 *)sendbuf, packet_size); if (err) { err_handle(err); } marker = 0; /* not the first packet of talkspurt */ rtp_interval -= delay; nexttime += delay; gettimeofday(&now_tv, NULL); now = (now_tv.tv_sec + now_tv.tv_usec / 1000000.); err = get_rtcp_timeout(sid, &timeout_tv); if (err) { err_handle(err); } while (now < nexttime) { /* send next packet until now >= nexttime */ //printf("now = %lf\n", now); //printf("nexttime = %lf\n", nexttime); if (time_expire(&timeout_tv, &now_tv)) { err = rtp_check_on_expire(); if (err) { err_handle(err); } err = get_rtcp_timeout(sid, &timeout_tv); if (err) { err_handle(err); } printf("timeval_to_double(timeout_tv) = %lf\n", timeval_to_double(timeout_tv)); } /* BECAREFUL, if we disable RTCP, the timeval we get will be 0 */ if (timeval_to_double(timeout_tv) == 0 || nexttime < timeval_to_double(timeout_tv)) { nexttime_tv = double_to_timeval(nexttime - now); } else { nexttime_tv = double_to_timeval(timeval_to_double(timeout_tv) - now); } if (select(nfds + 1, &rfds, (fd_set *)0, (fd_set *)0, &nexttime_tv) < 0) { if (errno == EINTR) continue; else { printf("nexttime_tv.tv_sec = %ld\n", nexttime_tv.tv_sec); printf("nexttime_tv.tv_usec = %ld\n", nexttime_tv.tv_usec); printf("select error: %d\n", errno); //exit(1); } } if (FD_ISSET(rtp_sockt, &rfds)) { err = on_receive(sid, rtp_sockt, recvbuf, &recbuflen); if (err) { err_handle(err); } } else if (FD_ISSET(rtcp_sockt, &rfds)) { err = on_receive(sid, rtcp_sockt, recvbuf, &recbuflen); if (err) { err_handle(err); } adapt_bw(get_recent_recv_loosrate(sid, &ssrc)); } gettimeofday(&now_tv, NULL); now = (now_tv.tv_sec + now_tv.tv_usec / 1000000.); } // while(now < nexttime) } // while (interval) err = rtp_close_connection(sid, reason); if (err) { err_handle(err); } err = rtp_delete(sid); if (err) { err_handle(err); } return 0; }
static int rtsp_read_header(AVFormatContext *s, AVFormatParameters *ap) { RTSPState *rt = s->priv_data; char host[1024], path[1024], tcpname[1024], cmd[2048], *option_list, *option; URLContext *rtsp_hd; int port, ret, err; RTSPHeader reply1, *reply = &reply1; unsigned char *content = NULL; int protocol_mask = 0; /* extract hostname and port */ url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, path, sizeof(path), s->filename); if (port < 0) port = RTSP_DEFAULT_PORT; /* search for options */ option_list = strchr(path, '?'); if (option_list) { /* remove the options from the path */ *option_list++ = 0; while(option_list) { /* move the option pointer */ option = option_list; option_list = strchr(option_list, '&'); if (option_list) *(option_list++) = 0; /* handle the options */ if (strcmp(option, "udp") == 0) protocol_mask = (1<< RTSP_PROTOCOL_RTP_UDP); else if (strcmp(option, "multicast") == 0) protocol_mask = (1<< RTSP_PROTOCOL_RTP_UDP_MULTICAST); else if (strcmp(option, "tcp") == 0) protocol_mask = (1<< RTSP_PROTOCOL_RTP_TCP); } } if (!protocol_mask) protocol_mask = (1 << RTSP_PROTOCOL_RTP_LAST) - 1; /* open the tcp connexion */ snprintf(tcpname, sizeof(tcpname), "tcp://%s:%d", host, port); if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0) return AVERROR(EIO); rt->rtsp_hd = rtsp_hd; rt->seq = 0; /* describe the stream */ snprintf(cmd, sizeof(cmd), "DESCRIBE %s RTSP/1.0\r\n" "Accept: application/sdp\r\n", s->filename); rtsp_send_cmd(s, cmd, reply, &content); if (!content) { err = AVERROR_INVALIDDATA; goto fail; } if (reply->status_code != RTSP_STATUS_OK) { err = AVERROR_INVALIDDATA; goto fail; } /* now we got the SDP description, we parse it */ ret = sdp_parse(s, (const char *)content); av_freep(&content); if (ret < 0) { err = AVERROR_INVALIDDATA; goto fail; } do { int protocol = ff_log2_tab[protocol_mask & ~(protocol_mask - 1)]; err = make_setup_request(s, host, port, protocol); if (err < 0) goto fail; protocol_mask &= ~(1 << protocol); if (protocol_mask == 0 && err == 1) { err = AVERROR(FF_NETERROR(EPROTONOSUPPORT)); goto fail; } } while (err); rt->state = RTSP_STATE_IDLE; rt->seek_timestamp = 0; /* default is to start stream at position zero */ if (ap->initial_pause) { /* do not start immediately */ } else { if (rtsp_read_play(s) < 0) { err = AVERROR_INVALIDDATA; goto fail; } } return 0; fail: rtsp_close_streams(rt); av_freep(&content); url_close(rt->rtsp_hd); return err; }
char *janus_sdp_anonymize(const char *sdp) { if(sdp == NULL) return NULL; sdp_session_t *anon = NULL; sdp_parser_t *parser = sdp_parse(home, sdp, strlen(sdp), 0); if(!(anon = sdp_session(parser))) { JANUS_LOG(LOG_ERR, "Error parsing/merging SDP: %s\n", sdp_parsing_error(parser)); sdp_parser_free(parser); return NULL; } /* c= */ if(anon->sdp_connection && anon->sdp_connection->c_address) { anon->sdp_connection->c_address = "1.1.1.1"; } /* a= */ if(anon->sdp_attributes) { /* These are attributes we handle ourselves, the plugins don't need them */ while(sdp_attribute_find(anon->sdp_attributes, "ice-ufrag")) sdp_attribute_remove(&anon->sdp_attributes, "ice-ufrag"); while(sdp_attribute_find(anon->sdp_attributes, "ice-pwd")) sdp_attribute_remove(&anon->sdp_attributes, "ice-pwd"); while(sdp_attribute_find(anon->sdp_attributes, "ice-options")) sdp_attribute_remove(&anon->sdp_attributes, "ice-options"); while(sdp_attribute_find(anon->sdp_attributes, "fingerprint")) sdp_attribute_remove(&anon->sdp_attributes, "fingerprint"); while(sdp_attribute_find(anon->sdp_attributes, "group")) sdp_attribute_remove(&anon->sdp_attributes, "group"); while(sdp_attribute_find(anon->sdp_attributes, "msid-semantic")) sdp_attribute_remove(&anon->sdp_attributes, "msid-semantic"); } /* m= */ if(anon->sdp_media) { int audio = 0, video = 0; #ifdef HAVE_SCTP int data = 0; #endif sdp_media_t *m = anon->sdp_media; while(m) { if(m->m_type == sdp_media_audio && m->m_port > 0) { audio++; m->m_port = audio == 1 ? 1 : 0; } else if(m->m_type == sdp_media_video && m->m_port > 0) { video++; m->m_port = video == 1 ? 1 : 0; #ifdef HAVE_SCTP } else if(m->m_type == sdp_media_application) { if(m->m_proto_name != NULL && !strcasecmp(m->m_proto_name, "DTLS/SCTP") && m->m_port != 0) { data++; m->m_port = data == 1 ? 1 : 0; } else { m->m_port = 0; } #endif } else { m->m_port = 0; } /* c= */ if(m->m_connections) { sdp_connection_t *c = m->m_connections; while(c) { if(c->c_address) { c->c_address = "1.1.1.1"; } c = c->c_next; } } /* a= */ if(m->m_attributes) { /* These are attributes we handle ourselves, the plugins don't need them */ while(sdp_attribute_find(m->m_attributes, "ice-ufrag")) sdp_attribute_remove(&m->m_attributes, "ice-ufrag"); while(sdp_attribute_find(m->m_attributes, "ice-pwd")) sdp_attribute_remove(&m->m_attributes, "ice-pwd"); while(sdp_attribute_find(m->m_attributes, "ice-options")) sdp_attribute_remove(&m->m_attributes, "ice-options"); while(sdp_attribute_find(m->m_attributes, "crypto")) sdp_attribute_remove(&m->m_attributes, "crypto"); while(sdp_attribute_find(m->m_attributes, "fingerprint")) sdp_attribute_remove(&m->m_attributes, "fingerprint"); while(sdp_attribute_find(m->m_attributes, "setup")) sdp_attribute_remove(&m->m_attributes, "setup"); while(sdp_attribute_find(m->m_attributes, "connection")) sdp_attribute_remove(&m->m_attributes, "connection"); while(sdp_attribute_find(m->m_attributes, "group")) sdp_attribute_remove(&m->m_attributes, "group"); while(sdp_attribute_find(m->m_attributes, "mid")) sdp_attribute_remove(&m->m_attributes, "mid"); while(sdp_attribute_find(m->m_attributes, "msid")) sdp_attribute_remove(&m->m_attributes, "msid"); while(sdp_attribute_find(m->m_attributes, "msid-semantic")) sdp_attribute_remove(&m->m_attributes, "msid-semantic"); while(sdp_attribute_find(m->m_attributes, "rtcp")) sdp_attribute_remove(&m->m_attributes, "rtcp"); while(sdp_attribute_find(m->m_attributes, "rtcp-mux")) sdp_attribute_remove(&m->m_attributes, "rtcp-mux"); while(sdp_attribute_find(m->m_attributes, "candidate")) sdp_attribute_remove(&m->m_attributes, "candidate"); while(sdp_attribute_find(m->m_attributes, "ssrc")) sdp_attribute_remove(&m->m_attributes, "ssrc"); while(sdp_attribute_find(m->m_attributes, "ssrc-group")) sdp_attribute_remove(&m->m_attributes, "ssrc-group"); while(sdp_attribute_find(m->m_attributes, "extmap")) /* TODO Actually implement RTP extensions */ sdp_attribute_remove(&m->m_attributes, "extmap"); while(sdp_attribute_find(m->m_attributes, "sctpmap")) sdp_attribute_remove(&m->m_attributes, "sctpmap"); } if(m->m_type != sdp_media_application && m->m_mode == sdp_sendrecv) { /* FIXME sendrecv hack: sofia-sdp doesn't print sendrecv, but we want it to */ sdp_attribute_t *fakedir = calloc(1, sizeof(sdp_attribute_t)); fakedir->a_size = sizeof(sdp_attribute_t); fakedir->a_name = g_strdup("jfmod"); fakedir->a_value = g_strdup("sr"); sdp_attribute_append(&m->m_attributes, fakedir); } m = m->m_next; } } char buf[BUFSIZE]; sdp_printer_t *printer = sdp_print(home, anon, buf, BUFSIZE, 0); if(sdp_message(printer)) { int retval = sdp_message_size(printer); sdp_printer_free(printer); sdp_parser_free(parser); /* FIXME Take care of the sendrecv hack, if needed */ char *replace = strstr(buf, "a=jfmod:sr"); while(replace != NULL) { memcpy(replace, "a=sendrecv", strlen("a=sendrecv")); replace++; replace = strstr(replace, "a=jfmod:sr"); } JANUS_LOG(LOG_VERB, " -------------------------------------------\n"); JANUS_LOG(LOG_VERB, " >> Anonymized (%zu --> %d bytes)\n", strlen(sdp), retval); JANUS_LOG(LOG_VERB, " -------------------------------------------\n"); JANUS_LOG(LOG_VERB, "%s\n", buf); return g_strdup(buf); } else { JANUS_LOG(LOG_ERR, "Error anonymizing SDP: %s\n", sdp_printing_error(printer)); sdp_printer_free(printer); sdp_parser_free(parser); return NULL; } }
char *janus_sdp_merge(janus_ice_handle *handle, const char *origsdp) { if(handle == NULL || origsdp == NULL) return NULL; sdp_session_t *anon = NULL; sdp_parser_t *parser = sdp_parse(home, origsdp, strlen(origsdp), 0); if(!(anon = sdp_session(parser))) { JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error parsing/merging SDP: %s\n", handle->handle_id, sdp_parsing_error(parser)); sdp_parser_free(parser); return NULL; } /* Prepare SDP to merge */ gchar buffer[512]; memset(buffer, 0, 512); char *sdp = (char*)calloc(BUFSIZE, sizeof(char)); if(sdp == NULL) { JANUS_LOG(LOG_FATAL, "Memory error!\n"); sdp_parser_free(parser); return NULL; } sdp[0] = '\0'; /* FIXME Any Plan B to take into account? */ int planb = strstr(origsdp, "a=planb:") ? 1 : 0; if(planb) { janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B); } else { janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B); } /* Version v= */ g_strlcat(sdp, "v=0\r\n", BUFSIZE); /* Origin o= */ if(anon->sdp_origin) { g_snprintf(buffer, 512, "o=%s %"SCNu64" %"SCNu64" IN IP4 127.0.0.1\r\n", /* FIXME Should we fix the address? */ anon->sdp_origin->o_username ? anon->sdp_origin->o_username : "******", anon->sdp_origin->o_id, anon->sdp_origin->o_version); g_strlcat(sdp, buffer, BUFSIZE); } else { gint64 sessid = janus_get_monotonic_time(); gint64 version = sessid; /* FIXME This needs to be increased when it changes, so time should be ok */ g_snprintf(buffer, 512, "o=%s %"SCNi64" %"SCNi64" IN IP4 127.0.0.1\r\n", /* FIXME Should we fix the address? */ "-", sessid, version); g_strlcat(sdp, buffer, BUFSIZE); } /* Session name s= */ if(anon->sdp_subject && strlen(anon->sdp_subject) > 0) { g_snprintf(buffer, 512, "s=%s\r\n", anon->sdp_subject); } else { g_snprintf(buffer, 512, "s=%s\r\n", "Meetecho Janus"); } g_strlcat(sdp, buffer, BUFSIZE); /* Timing t= */ g_snprintf(buffer, 512, "t=%lu %lu\r\n", anon->sdp_time ? anon->sdp_time->t_start : 0, anon->sdp_time ? anon->sdp_time->t_stop : 0); g_strlcat(sdp, buffer, BUFSIZE); /* ICE Full or Lite? */ if(janus_ice_is_ice_lite_enabled()) { /* Janus is acting in ICE Lite mode, advertize this */ g_strlcat(sdp, "a=ice-lite\r\n", BUFSIZE); } /* bundle: add new global attribute */ int audio = (strstr(origsdp, "m=audio") != NULL); int video = (strstr(origsdp, "m=video") != NULL); #ifdef HAVE_SCTP int data = (strstr(origsdp, "DTLS/SCTP") && !strstr(origsdp, " 0 DTLS/SCTP")); #else int data = 0; #endif g_strlcat(sdp, "a=group:BUNDLE", BUFSIZE); if(audio) { g_snprintf(buffer, 512, " %s", handle->audio_mid ? handle->audio_mid : "audio"); g_strlcat(sdp, buffer, BUFSIZE); } if(video) { g_snprintf(buffer, 512, " %s", handle->video_mid ? handle->video_mid : "video"); g_strlcat(sdp, buffer, BUFSIZE); } if(data) { g_snprintf(buffer, 512, " %s", handle->data_mid ? handle->data_mid : "data"); g_strlcat(sdp, buffer, BUFSIZE); } g_strlcat(sdp, "\r\n", BUFSIZE); /* msid-semantic: add new global attribute */ g_strlcat(sdp, "a=msid-semantic: WMS janus\r\n", BUFSIZE); char wms[BUFSIZE]; memset(wms, 0, BUFSIZE); g_strlcat(wms, "WMS", BUFSIZE); /* Copy other global attributes, if any */ if(anon->sdp_attributes) { sdp_attribute_t *a = anon->sdp_attributes; while(a) { if(a->a_value == NULL) { g_snprintf(buffer, 512, "a=%s\r\n", a->a_name); g_strlcat(sdp, buffer, BUFSIZE); } else { g_snprintf(buffer, 512, "a=%s:%s\r\n", a->a_name, a->a_value); g_strlcat(sdp, buffer, BUFSIZE); } a = a->a_next; } } gboolean ipv6 = strstr(janus_get_public_ip(), ":") != NULL; /* Media lines now */ if(anon->sdp_media) { int audio = 0, video = 0; #ifdef HAVE_SCTP int data = 0; #endif sdp_media_t *m = anon->sdp_media; janus_ice_stream *stream = NULL; while(m) { if(m->m_type == sdp_media_audio && m->m_port > 0) { audio++; if(audio > 1 || !handle->audio_id) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (we have %d audio lines, and the id is %d)\n", handle->handle_id, audio, handle->audio_id); g_strlcat(sdp, "m=audio 0 RTP/SAVPF 0\r\n", BUFSIZE); /* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */ g_snprintf(buffer, 512, "c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip()); g_strlcat(sdp, buffer, BUFSIZE); m = m->m_next; continue; } /* Audio */ stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id)); if(stream == NULL) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (invalid stream %d)\n", handle->handle_id, handle->audio_id); g_strlcat(sdp, "m=audio 0 RTP/SAVPF 0\r\n", BUFSIZE); /* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */ g_snprintf(buffer, 512, "c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip()); g_strlcat(sdp, buffer, BUFSIZE); m = m->m_next; continue; } g_strlcat(sdp, "m=audio 1 RTP/SAVPF", BUFSIZE); } else if(m->m_type == sdp_media_video && m->m_port > 0) { video++; gint id = handle->video_id; if(id == 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) id = handle->audio_id > 0 ? handle->audio_id : handle->video_id; if(video > 1 || !id) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (we have %d video lines, and the id is %d)\n", handle->handle_id, video, janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) ? handle->audio_id : handle->video_id); g_strlcat(sdp, "m=video 0 RTP/SAVPF 0\r\n", BUFSIZE); /* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */ g_snprintf(buffer, 512, "c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip()); g_strlcat(sdp, buffer, BUFSIZE); m = m->m_next; continue; } /* Video */ stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id)); if(stream == NULL) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (invalid stream %d)\n", handle->handle_id, id); g_strlcat(sdp, "m=video 0 RTP/SAVPF 0\r\n", BUFSIZE); /* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */ g_snprintf(buffer, 512, "c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip()); g_strlcat(sdp, buffer, BUFSIZE); m = m->m_next; continue; } g_strlcat(sdp, "m=video 1 RTP/SAVPF", BUFSIZE); #ifdef HAVE_SCTP } else if(m->m_type == sdp_media_application) { /* Is this SCTP for DataChannels? */ if(m->m_port > 0 && m->m_proto_name != NULL && !strcasecmp(m->m_proto_name, "DTLS/SCTP") && m->m_port > 0) { /* Yep */ data++; gint id = handle->data_id; if(id == 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) id = handle->audio_id > 0 ? handle->audio_id : handle->video_id; if(data > 1 || !id) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (we have %d SCTP lines, and the id is %d)\n", handle->handle_id, data, id); g_snprintf(buffer, 512, "m=%s 0 %s 0\r\n", m->m_type_name, m->m_proto_name); g_strlcat(sdp, buffer, BUFSIZE); /* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */ g_snprintf(buffer, 512, "c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip()); g_strlcat(sdp, buffer, BUFSIZE); m = m->m_next; continue; } /* SCTP */ stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id)); if(stream == NULL) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (invalid stream %d)\n", handle->handle_id, id); g_snprintf(buffer, 512, "m=%s 0 %s 0\r\n", m->m_type_name, m->m_proto_name); g_strlcat(sdp, buffer, BUFSIZE); /* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */ g_snprintf(buffer, 512, "c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip()); g_strlcat(sdp, buffer, BUFSIZE); m = m->m_next; continue; } g_strlcat(sdp, "m=application 1 DTLS/SCTP", BUFSIZE); } else { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping unsupported application media line...\n", handle->handle_id); g_snprintf(buffer, 512, "m=%s 0 %s 0\r\n", m->m_type_name, m->m_proto_name); g_strlcat(sdp, buffer, BUFSIZE); m = m->m_next; continue; } #endif } else { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id); g_snprintf(buffer, 512, "m=%s 0 %s 0\r\n", m->m_type_name, m->m_proto_name); g_strlcat(sdp, buffer, BUFSIZE); /* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */ g_snprintf(buffer, 512, "c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip()); g_strlcat(sdp, buffer, BUFSIZE); m = m->m_next; continue; } /* Add formats now */ if(!m->m_rtpmaps) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] No RTP maps?? trying formats...\n", handle->handle_id); if(!m->m_format) { JANUS_LOG(LOG_ERR, "[%"SCNu64"] No formats either?? this sucks!\n", handle->handle_id); g_strlcat(sdp, " 0", BUFSIZE); /* FIXME Won't work apparently */ } else { sdp_list_t *fmt = m->m_format; while(fmt) { g_snprintf(buffer, 512, " %s", fmt->l_text); g_strlcat(sdp, buffer, BUFSIZE); fmt = fmt->l_next; } } } else { sdp_rtpmap_t *r = m->m_rtpmaps; while(r) { g_snprintf(buffer, 512, " %d", r->rm_pt); g_strlcat(sdp, buffer, BUFSIZE); r = r->rm_next; } } g_strlcat(sdp, "\r\n", BUFSIZE); /* Media connection c= */ g_snprintf(buffer, 512, "c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip()); g_strlcat(sdp, buffer, BUFSIZE); /* Any bandwidth? */ if(m->m_bandwidths) { g_snprintf(buffer, 512, "b=%s:%lu\r\n", /* FIXME Are we doing this correctly? */ m->m_bandwidths->b_modifier_name ? m->m_bandwidths->b_modifier_name : "AS", m->m_bandwidths->b_value); g_strlcat(sdp, buffer, BUFSIZE); } /* a=mid:(audio|video|data) */ switch(m->m_type) { case sdp_media_audio: g_snprintf(buffer, 512, "a=mid:%s\r\n", handle->audio_mid ? handle->audio_mid : "audio"); break; case sdp_media_video: g_snprintf(buffer, 512, "a=mid:%s\r\n", handle->video_mid ? handle->video_mid : "video"); break; #ifdef HAVE_SCTP case sdp_media_application: /* FIXME sctpmap and webrtc-datachannel should be dynamic */ g_snprintf(buffer, 512, "a=mid:%s\r\na=sctpmap:5000 webrtc-datachannel 16\r\n", handle->data_mid ? handle->data_mid : "data"); break; #endif default: break; } g_strlcat(sdp, buffer, BUFSIZE); if(m->m_type != sdp_media_application) { /* What is the direction? */ switch(m->m_mode) { case sdp_sendonly: g_strlcat(sdp, "a=sendonly\r\n", BUFSIZE); break; case sdp_recvonly: g_strlcat(sdp, "a=recvonly\r\n", BUFSIZE); break; case sdp_inactive: g_strlcat(sdp, "a=inactive\r\n", BUFSIZE); break; case sdp_sendrecv: default: g_strlcat(sdp, "a=sendrecv\r\n", BUFSIZE); break; } /* rtcp-mux */ g_snprintf(buffer, 512, "a=rtcp-mux\n"); g_strlcat(sdp, buffer, BUFSIZE); /* RTP maps */ if(m->m_rtpmaps) { sdp_rtpmap_t *rm = NULL; for(rm = m->m_rtpmaps; rm; rm = rm->rm_next) { g_snprintf(buffer, 512, "a=rtpmap:%u %s/%lu%s%s\r\n", rm->rm_pt, rm->rm_encoding, rm->rm_rate, rm->rm_params ? "/" : "", rm->rm_params ? rm->rm_params : ""); g_strlcat(sdp, buffer, BUFSIZE); } for(rm = m->m_rtpmaps; rm; rm = rm->rm_next) { if(rm->rm_fmtp) { g_snprintf(buffer, 512, "a=fmtp:%u %s\r\n", rm->rm_pt, rm->rm_fmtp); g_strlcat(sdp, buffer, BUFSIZE); } } } } /* ICE ufrag and pwd, DTLS fingerprint setup and connection a= */ gchar *ufrag = NULL; gchar *password = NULL; nice_agent_get_local_credentials(handle->agent, stream->stream_id, &ufrag, &password); memset(buffer, 0, 100); g_snprintf(buffer, 512, "a=ice-ufrag:%s\r\n" "a=ice-pwd:%s\r\n" "a=ice-options:trickle\r\n" "a=fingerprint:sha-256 %s\r\n" "a=setup:%s\r\n" "a=connection:new\r\n", ufrag, password, janus_dtls_get_local_fingerprint(), janus_get_dtls_srtp_role(stream->dtls_role)); if(ufrag != NULL) g_free(ufrag); ufrag = NULL; if(password != NULL) g_free(password); password = NULL; g_strlcat(sdp, buffer, BUFSIZE); /* Copy existing media attributes, if any */ if(m->m_attributes) { sdp_attribute_t *a = m->m_attributes; while(a) { if(!strcmp(a->a_name, "planb")) { /* Skip the fake planb attribute, it's for internal use only */ a = a->a_next; continue; } if(a->a_value == NULL) { g_snprintf(buffer, 512, "a=%s\r\n", a->a_name); g_strlcat(sdp, buffer, BUFSIZE); } else { g_snprintf(buffer, 512, "a=%s:%s\r\n", a->a_name, a->a_value); g_strlcat(sdp, buffer, BUFSIZE); } a = a->a_next; } } /* Add last attributes, rtcp and ssrc (msid) */ if(!planb) { /* Single SSRC */ if(m->m_type == sdp_media_audio && m->m_mode != sdp_inactive && m->m_mode != sdp_recvonly) { g_snprintf(buffer, 512, "a=ssrc:%"SCNu32" cname:janusaudio\r\n" "a=ssrc:%"SCNu32" msid:janus janusa0\r\n" "a=ssrc:%"SCNu32" mslabel:janus\r\n" "a=ssrc:%"SCNu32" label:janusa0\r\n", stream->audio_ssrc, stream->audio_ssrc, stream->audio_ssrc, stream->audio_ssrc); g_strlcat(sdp, buffer, BUFSIZE); } else if(m->m_type == sdp_media_video && m->m_mode != sdp_inactive && m->m_mode != sdp_recvonly) { g_snprintf(buffer, 512, "a=ssrc:%"SCNu32" cname:janusvideo\r\n" "a=ssrc:%"SCNu32" msid:janus janusv0\r\n" "a=ssrc:%"SCNu32" mslabel:janus\r\n" "a=ssrc:%"SCNu32" label:janusv0\r\n", stream->video_ssrc, stream->video_ssrc, stream->video_ssrc, stream->video_ssrc); g_strlcat(sdp, buffer, BUFSIZE); } } else { /* Multiple SSRCs */ char mslabel[255]; memset(mslabel, 0, 255); if(m->m_attributes) { char id[256]; uint32_t ssrc = 0; sdp_attribute_t *a = m->m_attributes; while(a) { if(a->a_name == NULL || a->a_value == NULL || strcmp(a->a_name, "planb")) { a = a->a_next; continue; } if(sscanf(a->a_value, "%255s %"SCNu32"", id, &ssrc) != 2) { JANUS_LOG(LOG_ERR, "Error parsing 'planb' attribute, skipping it...\n"); a = a->a_next; continue; } JANUS_LOG(LOG_VERB, "Parsing 'planb' attribute: %s\n", a->a_value); /* Add proper SSRC attributes */ if(m->m_type == sdp_media_audio) { g_snprintf(buffer, 512, "a=ssrc:%"SCNu32" cname:%saudio\r\n" "a=ssrc:%"SCNu32" msid:%s %sa0\r\n" "a=ssrc:%"SCNu32" mslabel:%s\r\n" "a=ssrc:%"SCNu32" label:%sa0\r\n", ssrc, id, ssrc, id, id, ssrc, id, ssrc, id); } else if(m->m_type == sdp_media_video) { g_snprintf(buffer, 512, "a=ssrc:%"SCNu32" cname:%svideo\r\n" "a=ssrc:%"SCNu32" msid:%s %sv0\r\n" "a=ssrc:%"SCNu32" mslabel:%s\r\n" "a=ssrc:%"SCNu32" label:%sv0\r\n", ssrc, id, ssrc, id, id, ssrc, id, ssrc, id); } g_strlcat(sdp, buffer, BUFSIZE); /* Add to msid-semantic, if needed */ if(!strstr(wms, id)) { g_snprintf(mslabel, 255, " %s", id); g_strlcat(wms, mslabel, BUFSIZE); } /* Go on */ a = a->a_next; } } } /* And now the candidates */ janus_ice_candidates_to_sdp(handle, sdp, stream->stream_id, 1); if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) && m->m_type != sdp_media_application) janus_ice_candidates_to_sdp(handle, sdp, stream->stream_id, 2); /* Next */ m = m->m_next; } } /* Do we need to update the msid-semantic attribute? */ if(planb) { sdp = janus_string_replace(sdp, "WMS janus", wms); } sdp_parser_free(parser); JANUS_LOG(LOG_VERB, " -------------------------------------------\n"); JANUS_LOG(LOG_VERB, " >> Merged (%zu --> %zu bytes)\n", strlen(origsdp), strlen(sdp)); JANUS_LOG(LOG_VERB, " -------------------------------------------\n"); JANUS_LOG(LOG_VERB, "%s\n", sdp); return sdp; }